From bdcfed550636bfb0413b243ed90e6e72ae5c6a29 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 25 Jan 2025 14:27:20 +0800 Subject: [PATCH 01/41] fix: failed to handle images in draft. --- YaeBlog/Processors/ImagePostRenderProcessor.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/YaeBlog/Processors/ImagePostRenderProcessor.cs b/YaeBlog/Processors/ImagePostRenderProcessor.cs index f64a03a..7b02d86 100644 --- a/YaeBlog/Processors/ImagePostRenderProcessor.cs +++ b/YaeBlog/Processors/ImagePostRenderProcessor.cs @@ -7,7 +7,8 @@ using YaeBlog.Models; namespace YaeBlog.Processors; -public class ImagePostRenderProcessor(ILogger logger, +public class ImagePostRenderProcessor( + ILogger logger, IOptions options) : IPostRenderProcessor { @@ -29,22 +30,27 @@ public class ImagePostRenderProcessor(ILogger logger, if (attr is not null) { logger.LogDebug("Found image link: '{}'", attr.Value); - attr.Value = GenerateImageLink(attr.Value, essay.FileName); + attr.Value = GenerateImageLink(attr.Value, essay.FileName, essay.IsDraft); } } + return essay.WithNewHtmlContent(html.DocumentElement.OuterHtml); } public string Name => nameof(ImagePostRenderProcessor); - private string GenerateImageLink(string filename, string essayFilename) + private string GenerateImageLink(string filename, string essayFilename, bool isDraft) { + // 如果图片路径中没有包含文件名 + // 则添加文件名 if (!filename.Contains(essayFilename)) { filename = Path.Combine(essayFilename, filename); } - filename = Path.Combine(_options.Root, "posts", filename); + filename = isDraft + ? Path.Combine(_options.Root, "drafts", filename) + : Path.Combine(_options.Root, "posts", filename); if (!Path.Exists(filename)) { From e1c5362cf5b5212685d47bcc5fc2a88699ef95e9 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 25 Jan 2025 14:36:12 +0800 Subject: [PATCH 02/41] blog: adjust tags for some essays --- YaeBlog/source/posts/2021-final.md | 3 ++- YaeBlog/source/posts/2022-final.md | 3 ++- YaeBlog/source/posts/2022-summer-vacation.md | 2 +- YaeBlog/source/posts/2023-final.md | 3 ++- YaeBlog/source/posts/big-homework.md | 2 +- YaeBlog/source/posts/daily-linux-0.md | 2 +- YaeBlog/source/posts/daily-linux-1.md | 2 +- YaeBlog/source/posts/daily-linux-2.md | 2 +- YaeBlog/source/posts/daily-linux-3.md | 2 +- YaeBlog/source/posts/daily-linux-4.md | 2 +- YaeBlog/source/posts/laptop-for-computer.md | 2 +- 11 files changed, 14 insertions(+), 11 deletions(-) diff --git a/YaeBlog/source/posts/2021-final.md b/YaeBlog/source/posts/2021-final.md index e29deb8..619d73a 100644 --- a/YaeBlog/source/posts/2021-final.md +++ b/YaeBlog/source/posts/2021-final.md @@ -2,7 +2,8 @@ title: 2021年终总结 date: 2022-01-12 16:27:19 tags: - - 随笔 + - 杂谈 + - 年终总结 --- 2021年已经过去,2022年已经来临。每每一年开始的时候,我都会展开一张纸或者新建一个文档,思量着又是一年时光,也该同诸大杂志一般,写几句意味深长的话语,怀念过去的时光,也祝福未来的自己。可往往脑海中已是三万字的长篇,落在笔头却又是一个字都没有了。 diff --git a/YaeBlog/source/posts/2022-final.md b/YaeBlog/source/posts/2022-final.md index f9c31f8..91637e6 100644 --- a/YaeBlog/source/posts/2022-final.md +++ b/YaeBlog/source/posts/2022-final.md @@ -1,7 +1,8 @@ --- title: 2022年终总结 tags: - - 随笔 + - 杂谈 + - 年终总结 date: 2022-12-30 14:58:12 --- diff --git a/YaeBlog/source/posts/2022-summer-vacation.md b/YaeBlog/source/posts/2022-summer-vacation.md index 3fab150..39f9d13 100644 --- a/YaeBlog/source/posts/2022-summer-vacation.md +++ b/YaeBlog/source/posts/2022-summer-vacation.md @@ -1,7 +1,7 @@ --- title: 2022年暑假碎碎念 tags: - - 随笔 + - 杂谈 typora-root-url: 2022-summer-vacation date: 2022-08-22 15:39:13 --- diff --git a/YaeBlog/source/posts/2023-final.md b/YaeBlog/source/posts/2023-final.md index 1cc46f6..312097a 100644 --- a/YaeBlog/source/posts/2023-final.md +++ b/YaeBlog/source/posts/2023-final.md @@ -1,7 +1,8 @@ --- title: 2023年年终总结 tags: - - 随笔 + - 杂谈 + - 年终总结 date: 2024-2-29 20:18:19 --- diff --git a/YaeBlog/source/posts/big-homework.md b/YaeBlog/source/posts/big-homework.md index e699423..d763876 100644 --- a/YaeBlog/source/posts/big-homework.md +++ b/YaeBlog/source/posts/big-homework.md @@ -1,7 +1,7 @@ --- title: 人生代码大作业初体验 tags: - - 随笔 + - 杂谈 typora-root-url: big-homework date: 2022-07-27 11:34:49 --- diff --git a/YaeBlog/source/posts/daily-linux-0.md b/YaeBlog/source/posts/daily-linux-0.md index 413c1f3..98f1486 100644 --- a/YaeBlog/source/posts/daily-linux-0.md +++ b/YaeBlog/source/posts/daily-linux-0.md @@ -2,7 +2,7 @@ title: 日用Linux挑战 第0篇 初见Arch Linux tags: - Linux - - 随笔 + - 杂谈 date: 2023-01-15 22:23:08 typora-root-url: daily-linux-0 --- diff --git a/YaeBlog/source/posts/daily-linux-1.md b/YaeBlog/source/posts/daily-linux-1.md index c280fc3..2910e5e 100644 --- a/YaeBlog/source/posts/daily-linux-1.md +++ b/YaeBlog/source/posts/daily-linux-1.md @@ -2,7 +2,7 @@ title: 日用Linux挑战 第1篇 问题与挑战 tags: - Linux - - 随笔 + - 杂谈 date: 2023-03-08 22:37:29 --- diff --git a/YaeBlog/source/posts/daily-linux-2.md b/YaeBlog/source/posts/daily-linux-2.md index 04ebae4..3960298 100644 --- a/YaeBlog/source/posts/daily-linux-2.md +++ b/YaeBlog/source/posts/daily-linux-2.md @@ -1,7 +1,7 @@ --- title: 日用Linux挑战 第2篇 Wayland tags: - - 随笔 + - 杂谈 - Linux date: 2023-07-23 11:44:34 typora-root-url: daily-linux-2 diff --git a/YaeBlog/source/posts/daily-linux-3.md b/YaeBlog/source/posts/daily-linux-3.md index bf8bde1..ae507a4 100644 --- a/YaeBlog/source/posts/daily-linux-3.md +++ b/YaeBlog/source/posts/daily-linux-3.md @@ -1,7 +1,7 @@ --- title: 日用Linux挑战 第3篇 放弃Wayland tags: - - 随笔 + - 杂谈 - Linux typora-root-url: daily-linux-3 date: 2023-09-04 14:47:46 diff --git a/YaeBlog/source/posts/daily-linux-4.md b/YaeBlog/source/posts/daily-linux-4.md index 83d9c35..82b364d 100644 --- a/YaeBlog/source/posts/daily-linux-4.md +++ b/YaeBlog/source/posts/daily-linux-4.md @@ -2,7 +2,7 @@ title: 日用Linux挑战 第4篇 新的开始 tags: - Linux - - 随笔 + - 杂谈 date: 2024/03/09 14:00:00 --- diff --git a/YaeBlog/source/posts/laptop-for-computer.md b/YaeBlog/source/posts/laptop-for-computer.md index d5aaff2..022a018 100644 --- a/YaeBlog/source/posts/laptop-for-computer.md +++ b/YaeBlog/source/posts/laptop-for-computer.md @@ -1,7 +1,7 @@ --- title: 大学生用啥配置——计算机专业 tags: - - 随笔 + - 杂谈 typora-root-url: laptop-for-computer date: 2022-06-13 16:17:27 --- From b9c44408adabfb79e3040ba9401f1044bcf6e334 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 7 Mar 2025 16:52:59 +0800 Subject: [PATCH 03/41] fix: update build action to use podman --- .gitea/workflows/build.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index a728f9c..9015dfd 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -7,7 +7,7 @@ jobs: Build-Blog-Image: runs-on: archlinux steps: - - uses: https://git.rrricardo.top/actions/checkout@v4 + - uses: https://mirrors.rrricardo.top/actions/checkout.git@v4 name: Check out code with: lfs: true @@ -15,12 +15,15 @@ jobs: run: | cd YaeBlog dotnet publish + - name: Alias podman as docker + run: | + echo 'alias docker="podman"' >> $HOME/.bashrc - name: Build docker image run: | cd YaeBlog docker build . -t registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest - name: Login aliyun docker registry - uses: https://git.rrricardo.top/actions/login-action@v3 + uses: https://mirrors.rrricardo.top/actions/login-action.git@v3 with: registry: registry.cn-beijing.aliyuncs.com username: 初冬的朝阳 From dec2bc937ad5fd6c9a7828db2b0fa3c6a5aa9a64 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 7 Mar 2025 20:21:17 +0800 Subject: [PATCH 04/41] chore: remove unused steps in build actions. --- .gitea/workflows/build.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 9015dfd..9c331cd 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -15,9 +15,6 @@ jobs: run: | cd YaeBlog dotnet publish - - name: Alias podman as docker - run: | - echo 'alias docker="podman"' >> $HOME/.bashrc - name: Build docker image run: | cd YaeBlog From dcad453eb122940be183042ecf8c23eaa20885ba Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 8 Mar 2025 00:32:09 +0800 Subject: [PATCH 05/41] blog: hpc-2025-intro --- YaeBlog/source/posts/hpc-2025-intro.md | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 YaeBlog/source/posts/hpc-2025-intro.md diff --git a/YaeBlog/source/posts/hpc-2025-intro.md b/YaeBlog/source/posts/hpc-2025-intro.md new file mode 100644 index 0000000..c539db5 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-intro.md @@ -0,0 +1,33 @@ +--- +title: High Performance Computing 25 SP Introduction +date: 2025-03-08T00:31:54.5775860+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + +High performance computing is the use of supercomputing and computing clusters to solve advanced problems. + + + +> High Performance Computing: Software execution speedup. +> +> High Throughput Computing: Resource utilization efficiency. + +High performance computing: + +- Provide computer power. +- Full-Domain HPC = Full-Stack HPC + Full-Network HPC. + +Two key weapons: **partitioning** and **duplicating** to increase granularity. + +HPC History: + +- Mainframe computer +- Mini computer +- Cluster +- Grids +- Clouds + + + From 05ea729950a3df3cef52e4831aca848f212f32eb Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 14 Mar 2025 00:06:18 +0800 Subject: [PATCH 06/41] blog: hpc cpu architecture --- .../source/posts/hpc-2025-cpu-architecture.md | 100 ++++++++++++++++++ .../image-20250313184421305.png | 3 + .../image-20250313184732892.png | 3 + .../image-20250313190913475.png | 3 + 4 files changed, 109 insertions(+) create mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture.md create mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.png create mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.png create mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.png diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture.md b/YaeBlog/source/posts/hpc-2025-cpu-architecture.md new file mode 100644 index 0000000..ea89b29 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cpu-architecture.md @@ -0,0 +1,100 @@ +--- +title: High Performance Computing 2025 SP CPU Architecture +date: 2025-03-13T23:59:08.8167680+08:00 +tags: +- 学习资料 +- 高性能计算 +--- + +How to use the newly available transistors? + + + +Parallelsim: + +Instruction Level Parallelism(ILP): + +- **Implicit/transparent** to users/programmers. +- Instruction pipelining. +- Superscalar execution. +- Out of order execution. +- Register renaming. +- Speculative execution. +- Branch prediction. + +Task Level Parallelism(TLP): + +- **Explicit** to users/programmers. +- Multiple threads or processes executed simultaneously. +- Multi-core processors. + +Data Parallelism: + +- Vector processors and SIMD. + +Von Neumann Architecture: the **stored-program** concept. Three components: processor, memory and data path. + +Bandwidth: the gravity of modern computer system. + +## Instruction Pipelining + +Divide incoming instructions into a series of sequential steps performed by different processor unit to keep every part of the processor busy. + +Superscalar execution can execute more than one instruction during a clock cycle. + +Order of order execution. + +Very long instruction word(VLIW): allows programs to explicitly specify instructions to execute at the same time. + +EPIC: Explicit parallel instruction computing. + +Move the complexity of instruction scheduling from the CPU hardware to the software compiler: + +- Check dependencies between instructions. +- Assign instructions to the functional units. +- Determine when instructions are initiated placed together into a single word. + +![image-20250313184421305](./hpc-2025-cpu-architecture/image-20250313184421305.png) + +Comparisons between different architecture: + +![image-20250313184732892](./hpc-2025-cpu-architecture/image-20250313184732892.png) + +## Multi-Core Processor Gala + +Symmetric multiprocessing(SMP): a multiprocessor computer hardware and software architecture. + +Two or more identical processors are connected to a **single shared main memory** and have full access to all input and output devices. + +> Current trend: computer clusters, SMP computers connected with network. + +Multithreading: exploiting thread-level parallelism. + +Multithreading allows multiple threads to share the functional units of a single processor in an overlapping fashion **duplicating only private state**. A thread switch should be much more efficient than a process switch. + +Hardware approaches to multithreading: + +**fine-grained multithreading**: + +- Switches between threads on each clock. +- Hide the throughput losses that arise from the both short and long stalls. +- Disadvantages: slow down the execution of an individual thread. + +**Coarse-grained multithreading**: + +- Switch threads only on costly stalls. +- Limited in its ability to overcome throughput losses + +**Simultaneous multithreading(SMT)**: + +- A variation on fine-grained multithreading + +![image-20250313190913475](./hpc-2025-cpu-architecture/image-20250313190913475.png) + +## Data Parallelism: Vector Processors + +Provides high-level operations that work on vectors. + +Length of the array also varies depending on hardware. + +SIMD and its generalization in vector parallelism approach improved efficiency by the same operation be performed on multiple data elements. diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.png b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.png new file mode 100644 index 0000000..89925dc --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e3183c5ad4ee11b6dd924a0ed85ed9a13d3d61a78de741fcb649fe14def6473 +size 158027 diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.png b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.png new file mode 100644 index 0000000..90cf137 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9c4a4cee93475a3d73f5ed8bc97e5978ae5f099c754b4e94c2b8e5a1a2586cc +size 776759 diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.png b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.png new file mode 100644 index 0000000..6f3672e --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65afb4d57b7cd63940786cf43eb5b455e6ab9b19c48257f599a01dc36616352f +size 239063 From 7f3221fde94dec268d583c630805b24862638aed Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 14 Mar 2025 00:16:30 +0800 Subject: [PATCH 07/41] chore: fix build action and migrate to slnx. --- .gitea/workflows/build.yaml | 6 +++--- YaeBlog.sln | 41 ------------------------------------- YaeBlog.slnx | 14 +++++++++++++ 3 files changed, 17 insertions(+), 44 deletions(-) delete mode 100644 YaeBlog.sln create mode 100644 YaeBlog.slnx diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 9c331cd..ffab5e2 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -18,12 +18,12 @@ jobs: - name: Build docker image run: | cd YaeBlog - docker build . -t registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest + podman build . -t registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest - name: Login aliyun docker registry - uses: https://mirrors.rrricardo.top/actions/login-action.git@v3 + uses: https://mirrors.rrricardo.top/actions/podman-login.git@v1 with: registry: registry.cn-beijing.aliyuncs.com username: 初冬的朝阳 password: ${{ secrets.ALIYUN_PASSWORD }} - name: Push docker image - run: docker push registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest + run: podman push registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest diff --git a/YaeBlog.sln b/YaeBlog.sln deleted file mode 100644 index c4f8f9c..0000000 --- a/YaeBlog.sln +++ /dev/null @@ -1,41 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YaeBlog", "YaeBlog\YaeBlog.csproj", "{20438EFD-8DDE-43AF-92E2-76495C29233C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".gitea", ".gitea", "{9B5AAA29-37D8-454A-8D8F-3E6B6BCF38E6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ADBC3DA8-F65C-4B5D-A97A-DC351F8E6592}" - ProjectSection(SolutionItems) = preProject - .gitea\workflows\build.yaml = .gitea\workflows\build.yaml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{753B998C-1B9E-498F-B949-845CE86C4075}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitattributes = .gitattributes - .gitignore = .gitignore - README.md = README.md - LICENSE = LICENSE - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {20438EFD-8DDE-43AF-92E2-76495C29233C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {20438EFD-8DDE-43AF-92E2-76495C29233C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {20438EFD-8DDE-43AF-92E2-76495C29233C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {20438EFD-8DDE-43AF-92E2-76495C29233C}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {ADBC3DA8-F65C-4B5D-A97A-DC351F8E6592} = {9B5AAA29-37D8-454A-8D8F-3E6B6BCF38E6} - EndGlobalSection -EndGlobal diff --git a/YaeBlog.slnx b/YaeBlog.slnx new file mode 100644 index 0000000..53e014e --- /dev/null +++ b/YaeBlog.slnx @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + From 383dd416958e0fb5f5d9c98ac5d592fb55c31fa4 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 14 Mar 2025 00:23:43 +0800 Subject: [PATCH 08/41] fix: build actions. --- .gitea/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index ffab5e2..02ecccb 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -25,5 +25,6 @@ jobs: registry: registry.cn-beijing.aliyuncs.com username: 初冬的朝阳 password: ${{ secrets.ALIYUN_PASSWORD }} + auth_file_path: /auth.json - name: Push docker image run: podman push registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest From 4682dacc79f3e65ecf8131d07743e2a33d4bb69a Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 14 Mar 2025 00:29:33 +0800 Subject: [PATCH 09/41] fix: build actions. --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 02ecccb..910869b 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -25,6 +25,6 @@ jobs: registry: registry.cn-beijing.aliyuncs.com username: 初冬的朝阳 password: ${{ secrets.ALIYUN_PASSWORD }} - auth_file_path: /auth.json + auth_file_path: /etc/containers/auth.json - name: Push docker image run: podman push registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest From 043376c6d3d27bc171425378d01eea7f77b13d6c Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 14 Mar 2025 00:41:50 +0800 Subject: [PATCH 10/41] fix: build action. --- .gitea/workflows/build.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 910869b..15958d6 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -19,6 +19,9 @@ jobs: run: | cd YaeBlog podman build . -t registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest + - name: Workaround to make sure podman login succeed + run: | + mkdir /root/.docker - name: Login aliyun docker registry uses: https://mirrors.rrricardo.top/actions/podman-login.git@v1 with: From 132261831bcf90250718077b4233a73e89f39137 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Wed, 19 Mar 2025 20:59:34 +0800 Subject: [PATCH 11/41] blog: mlir-standalone --- YaeBlog/source/posts/mlir-standalone.md | 163 ++++++++++++++++++ .../image-20250319192618697.png | 3 + .../image-20250319194742171.png | 3 + .../image-20250319202218503.png | 3 + .../image-20250319202553644.png | 3 + .../image-20250319204057832.png | 3 + .../image-20250319204522857.png | 3 + .../image-20250319205520649.png | 3 + 8 files changed, 184 insertions(+) create mode 100644 YaeBlog/source/posts/mlir-standalone.md create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319192618697.png create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319194742171.png create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319202218503.png create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319202553644.png create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319204057832.png create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319204522857.png create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319205520649.png diff --git a/YaeBlog/source/posts/mlir-standalone.md b/YaeBlog/source/posts/mlir-standalone.md new file mode 100644 index 0000000..4d5f704 --- /dev/null +++ b/YaeBlog/source/posts/mlir-standalone.md @@ -0,0 +1,163 @@ +--- +title: 构建运行基于MLIR的独立项目 +date: 2025-03-19T20:57:31.1928528+08:00 +tags: +- 技术笔记 +- LLVM +--- + +MLIR是多层次中间表示形式(Multi-Level Intermediate Representation),是LLVM项目中提供的一项新编译器开发基础设施,使得编译器开发者能够在源代码和可执行代码之间定义多层IR来保留程序信息指导编译优化。本博客指导如何创建一个独立(out-of-tree)的MLIR项目。 + + + +## 编译LLVM和MLIR + +考虑到大多数的Linux发行版在打包LLVM时不会编译MLIR,因此自行编译安装包括MLIR项目的LLVM就成为开发独立MLIR项目的前置条件。 + +首先在GitHub上下载LLVM的源代码包,我这里选择最新的稳定版本`20.1.0`。 + +```shell +wget https://github.com/llvm/llvm-project/releases/download/llvmorg-20.1.0/llvm-project-20.1.0.src.tar.xz +``` + +下载之后解压进入,准备进行构建。 + +```shell +tar xvf llvm-project-20.1.0.src.tar.xz +cd llvm-project-20.1.0.src +``` + +创建`build`文件夹,使用下面的命令进行生成构建文件。在这里选择使用`Release`构建类型,安装的位置是`~/.local/share/llvm`文件夹,构建的项目包括`llvm`、`clang`和`mlir`三个项目,并指定使用系统上的`clang`和`clang++`编译器作为编译过程中使用的编译器。 + +```shell +mkdir build +cd build +cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="/home/ricardo/.local/share/llvm-20.1.0" -DLLVM_ENABLE_PROJECTS="llvm;clang;mlir" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_INSTALL_UTILS=true ../llvm +``` + +![image-20250319192618697](./mlir-standalone/image-20250319192618697.png) + +生成构建文件之后使用`ninja`进行构建。 + +```shell +ninja +``` + +![image-20250319194742171](./mlir-standalone/image-20250319194742171.png) + +构建在我的i5-13600K上大约需要20分钟。 + +构建完成之后进行安装。 + +```shell +ninja install +``` + +## 编译MLIR官方的独立项目 + +MLIR的官方提供了一个独立项目,项目文件夹在`mlir/examples/standalone`中,将这个文件夹中的内容复制到我们需要的地方,尝试使用上面构建的`mlir`进行构建。 + +```shell +cp -r ~/Downloads/llvm-project-20.1.0.src/mlir/examples/standalone mlir-standalone +cd mlir-standalone +``` + +### 不启用测试 + +编译过程中可能遇到的最大问题是`llvm-lit`,这个使用`python`编写的LLVM集成测试工具,在`standalone`的`README.md`中要求编译过程中使用`LLVM_EXTERNAL_LIT`变量指定到LLVM编译过程中生成的`llvm-lit`可执行文件。 + +> 也许就是因为`llvm-lit`是用Python撰写的,所以`llvm-lit`不会安装到`PREFIX`指定的位置。 + +不过我们可以禁用测试(笑)。在`CMakeLists.txt`文件中注释对于测试文件夹的添加: + +```cmake +add_subdirectory(include) +add_subdirectory(lib) +if(MLIR_ENABLE_BINDINGS_PYTHON) + message(STATUS "Enabling Python API") + add_subdirectory(python) +endif() +#add_subdirectory(test) +add_subdirectory(standalone-opt) +add_subdirectory(standalone-plugin) +add_subdirectory(standalone-translate) + +``` + +回到构建文件夹,使用如下的`cmake`指令生成构建文件。 + +```shell +export LLVM_DIR=/home/ricardo/.local/share/llvm-20.1.0 +cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir .. +``` + +可以顺利通过编译。 + +![image-20250319202218503](./mlir-standalone/image-20250319202218503.png) + +### 启用测试 + +但是测试还是非常重要的。我们尝试启动测试看看,取消对于测试文件夹的注释: + +```shell +rm -rf build && mkdir build && cd build +cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir .. +``` + +很好顺利报错,报错的提示是缺失`FileCheck`、`count`和`not`。 + +![image-20250319202553644](./mlir-standalone/image-20250319202553644.png) + +那么按照`README.md`中的提示添加上来自构建目录的`llvm-lit`会怎么样呢? + +```shell +rm -rf build && mkdir build && cd build +export LLVM_BUILD_DIR=/home/ricardo/Downloads/llvm-project-20.1.0.src/build +cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=$LLVM_BUILD_DIR/bin/llvm-lit .. +``` + +同样的报错,看来问题不是出在这里。 + +经过对于LLVM文档的仔细研究,发现原来是没有启动这个变量: + +![image-20250319204057832](./mlir-standalone/image-20250319204057832.png) + +遂修改最初的LLVM编译指令。 + +重新运行来自`README.md`的构建文件生成指令之后,测试也完美运行通过: + +```shell +ninja test-standalone +``` + +![image-20250319204522857](./mlir-standalone/image-20250319204522857.png) + +不过这个还是有一点令我不是特别满意,这依赖了一个来自于构建目录的工具`llvm-lit`,如果我编译安装的时候眼疾手快的删除了编译目录不就完蛋了。而且我都**standalone**了还依赖似乎有点说不过去? + +于是发现了一篇从`pip`上下载使用`llvm-lit`的[博客](https://medium.com/@mshockwave/using-llvm-lit-out-of-tree-5cddada85a78)和一个LLVM Discourse上面的[帖子](https://discourse.llvm.org/t/running-llvm-lit-on-external-project-test-file-derived-from-standalone-fails/67787),遂进行尝试。 + +首先在当前目录下创建一个虚拟环境,并下载安装`llvm-lit`。 + +```shell +python -m venv .llvm-lit +source .llvm-lit/bin/activate +pip install lit +``` + +不过这个库似乎没有提供运行入口点,需要我们手动创建一个可执行的`python`文件: + +```python +#!/usr/bin/env python +from lit.main import main +if __name__ == '__main__': + main() +``` + +然后尝试在`cmake`指令中修改为`lit`为这个可执行文件: + +```shell +cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=$(pwd)/../llvm-lit .. +ninja test-standalone +``` + +![image-20250319205520649](./mlir-standalone/image-20250319205520649.png) diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319192618697.png b/YaeBlog/source/posts/mlir-standalone/image-20250319192618697.png new file mode 100644 index 0000000..32d3974 --- /dev/null +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319192618697.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dce1e3bf8f40cb65ab1a75bd2640d8f40573c977de20c748d9ece4d3efbeb791 +size 76812 diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319194742171.png b/YaeBlog/source/posts/mlir-standalone/image-20250319194742171.png new file mode 100644 index 0000000..c03dbce --- /dev/null +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319194742171.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41c2aae547c0d4ab2ccd520bc7bb914222a95dfdb879f0166e624fa7edbccb79 +size 26105 diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319202218503.png b/YaeBlog/source/posts/mlir-standalone/image-20250319202218503.png new file mode 100644 index 0000000..a797831 --- /dev/null +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319202218503.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:270725669a377f83b2c134272f0f5489411ef549d8997600b3acb1bd37aac85f +size 19846 diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319202553644.png b/YaeBlog/source/posts/mlir-standalone/image-20250319202553644.png new file mode 100644 index 0000000..d8c5e37 --- /dev/null +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319202553644.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0398124dda06e47933e7631f3c1363a21eb3b32db05c709ba157819da69fa6b +size 114678 diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319204057832.png b/YaeBlog/source/posts/mlir-standalone/image-20250319204057832.png new file mode 100644 index 0000000..57dff91 --- /dev/null +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319204057832.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb677444bdc074b20d795d1ab134344e73082b3be206cb5507eb8ddc9677b679 +size 18618 diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319204522857.png b/YaeBlog/source/posts/mlir-standalone/image-20250319204522857.png new file mode 100644 index 0000000..224d2ea --- /dev/null +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319204522857.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0f8813f97e766aaf1f0fe67f1ff5d4c21d4584ed677de08da2edc9bca222a1f +size 48105 diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319205520649.png b/YaeBlog/source/posts/mlir-standalone/image-20250319205520649.png new file mode 100644 index 0000000..910ed77 --- /dev/null +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319205520649.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07320dbbb252cbf10e3ccde1b2ed06a1aed35dd128932cebdba387d9f88e599a +size 37427 From c293d2f6d780279bcde0289f8008b3155dd608c5 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Thu, 20 Mar 2025 20:51:49 +0800 Subject: [PATCH 12/41] blog: update mlir-standalone --- YaeBlog/source/posts/mlir-standalone.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/YaeBlog/source/posts/mlir-standalone.md b/YaeBlog/source/posts/mlir-standalone.md index 4d5f704..7943448 100644 --- a/YaeBlog/source/posts/mlir-standalone.md +++ b/YaeBlog/source/posts/mlir-standalone.md @@ -144,7 +144,7 @@ source .llvm-lit/bin/activate pip install lit ``` -不过这个库似乎没有提供运行入口点,需要我们手动创建一个可执行的`python`文件: +~~不过这个库似乎没有提供运行入口点,需要我们手动创建一个可执行的`python`文件:~~ ```python #!/usr/bin/env python @@ -153,10 +153,10 @@ if __name__ == '__main__': main() ``` -然后尝试在`cmake`指令中修改为`lit`为这个可执行文件: +经哥们纠正说`lit`包在某个版本之后会安装`lit`的可执行文件,在安装之后可以直接在命令行调用`lit`。因此在激活虚拟环境之后,`cmake`中直接在`LLVM_EXTERNAL_LIT`配置为`$(which lit)`即可。 ```shell -cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=$(pwd)/../llvm-lit .. +cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=$(which lit) .. ninja test-standalone ``` From 4df3b98e6dbf87f9699fb4ae4d96090e6d0db184 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Thu, 20 Mar 2025 22:35:19 +0800 Subject: [PATCH 13/41] blog: msbuild-generate-files blog: fix title in hpc-2025-cpu-architecture --- .../source/posts/hpc-2025-cpu-architecture.md | 2 +- .../source/posts/msbuild-generate-files.md | 117 ++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 YaeBlog/source/posts/msbuild-generate-files.md diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture.md b/YaeBlog/source/posts/hpc-2025-cpu-architecture.md index ea89b29..a77cb8b 100644 --- a/YaeBlog/source/posts/hpc-2025-cpu-architecture.md +++ b/YaeBlog/source/posts/hpc-2025-cpu-architecture.md @@ -1,5 +1,5 @@ --- -title: High Performance Computing 2025 SP CPU Architecture +title: High Performance Computing 25 SP CPU Architecture date: 2025-03-13T23:59:08.8167680+08:00 tags: - 学习资料 diff --git a/YaeBlog/source/posts/msbuild-generate-files.md b/YaeBlog/source/posts/msbuild-generate-files.md new file mode 100644 index 0000000..a823395 --- /dev/null +++ b/YaeBlog/source/posts/msbuild-generate-files.md @@ -0,0 +1,117 @@ +--- +title: 如何在MSBuild中复制生成的文件到发布目录中 +date: 2025-03-20T22:33:21.6955385+08:00 +tags: +- 技术笔记 +- dotnet +--- + + 如何使用`MSBuild`将构建过程中生成文件复制到生成目录中? + + + +### 遇到的问题 + +最近在尝试在`blazor`项目中使用`tailwindcss`作为`css`工具类的提供工具,而不是使用老旧的`bootstrap`框架,不过使用`tailwindcss`需要在项目构建时使用`tailwindcss`工具扫描文件中使用到的`css`属性并生成最终的`css`文件,这就带来了在构建时运行`tailwindcss`生成并复制到输出目录的需求。 + +由于我是使用`pnpm`作为前端管理工具,我在项目的`csproj`文件中添加了下面的`Target`来生成文件: + +```xml + + + + + + + + + + + + + + + + +``` + +这套生成逻辑在本地工作良好,但是却在CI上运行时出现了问题:`CI`上打包的`Docker`镜像中没有`tailwind.g.css`文件,导致最终部署的站点丢失了所有的格式。 + +### 产生问题的原因 + +经过反复实验,我发现只有在构建之前`wwwroot`目录中已经存在`tailwind.g.css`文件的情况下,`MSBuild`才会将生成的文件复制到最终的输出目录中。但是在`CI`环境下,因为使用`.gitignore`没有将`*.g.css`文件添加到代码管理,因此`CI`运行构建之前没有该文件,因此构建的结果中也没有该文件。 + +仔细研究`MSBuild`的文档和网络上的[分享](https://gist.github.com/BenVillalobos/c671baa1e32127f4ab582a5abd66b005),我意识到这是由于`MSBuild`的构建流程导致的,MSBuild`的构建流程分成两个大的阶段: + +- 评估阶段(Evaluation Phase) + + 在这个阶段,`MSBuild`将会运行读取所有的配置文件,创建需要的属性,展开所有的`glob`,建立好整个构建流程。 + +- 执行阶段(Execution Phase) + + 在这个阶段,`MSBuild`将按照上一阶段执行的属性执行实际的构建指令。 + +这两个阶段的划分就导致在生成阶段才生成的文件不会被包含在复制文件的指令中,因此他们不会被拷贝到最终的输出目录中。 + +>这和`cmake`的构建过程很像,首先调用`cmake`生成一些构建指令,在调用实际的构建指令构建二进制文件。 + +因此这类问题的推荐解决办法是手动将这些文件添加到构建流程中,即在`BeforeBuild`目标调用之前使用`Content`和`None`等项。 + +### 解决问题 + +总结上述的解决问题方法,我在上面的构建流程中添加了如下的`None`项: + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +在运行构建之后,在最终的`publish`文件夹的`wwroot`文件夹中就可以找到`tailwind.g.css`文件。 + +不过我还想进行一点优化,`MSBuild`文档中建议将自动生成的文件放在`IntermediateOutputPath`,也就是`obj`文件加中,因此这里尝试将`tailwind.g.css`文件生成到`IntermediateOuputPath`中,优化之后的`Target`项长这个样子: + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +经过测试,这套生成逻辑在`blazor`类库环境下也可以正常运行,类库的文件会被正确地生成到`wwwroot/_content//`文件夹下面。 + From 2b9c374e8cdcf3c6487930d231d75b807f430e08 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 22 Mar 2025 17:34:43 +0800 Subject: [PATCH 14/41] feat: beatify the blog content. Remove some unused font files and upgrade tailwindcss to v4.0.0. --- .gitignore | 2 +- YaeBlog/Components/App.razor | 2 +- YaeBlog/Extensions/AngleSharpExtensions.cs | 18 + .../EssayStylesPostRenderProcessor.cs | 47 +- YaeBlog/YaeBlog.csproj | 10 +- YaeBlog/package.json | 23 +- YaeBlog/pnpm-lock.yaml | 1107 ++++++----------- YaeBlog/tailwind.config.js | 9 - YaeBlog/wwwroot/fonts/bootstrap-icons.woff | Bin 176032 -> 0 bytes YaeBlog/wwwroot/fonts/bootstrap-icons.woff2 | 3 - YaeBlog/wwwroot/input.css | 3 - YaeBlog/wwwroot/tailwind.css | 1 + 12 files changed, 494 insertions(+), 731 deletions(-) create mode 100644 YaeBlog/Extensions/AngleSharpExtensions.cs delete mode 100644 YaeBlog/tailwind.config.js delete mode 100644 YaeBlog/wwwroot/fonts/bootstrap-icons.woff delete mode 100644 YaeBlog/wwwroot/fonts/bootstrap-icons.woff2 delete mode 100644 YaeBlog/wwwroot/input.css create mode 100644 YaeBlog/wwwroot/tailwind.css diff --git a/.gitignore b/.gitignore index f606da8..c9f783c 100644 --- a/.gitignore +++ b/.gitignore @@ -484,4 +484,4 @@ $RECYCLE.BIN/ *.swp # Tailwind auto-generated stylesheet -output.css +*.g.css diff --git a/YaeBlog/Components/App.razor b/YaeBlog/Components/App.razor index f6d881b..75b2658 100644 --- a/YaeBlog/Components/App.razor +++ b/YaeBlog/Components/App.razor @@ -8,7 +8,7 @@ - + diff --git a/YaeBlog/Extensions/AngleSharpExtensions.cs b/YaeBlog/Extensions/AngleSharpExtensions.cs new file mode 100644 index 0000000..a299ab8 --- /dev/null +++ b/YaeBlog/Extensions/AngleSharpExtensions.cs @@ -0,0 +1,18 @@ +using AngleSharp.Dom; + +namespace YaeBlog.Extensions; + +public static class AngleSharpExtensions +{ + public static IEnumerable EnumerateParentElements(this IElement element) + { + IElement? e = element.ParentElement; + + while (e is not null) + { + IElement c = e; + e = e.ParentElement; + yield return c; + } + } +} diff --git a/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs b/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs index 8329d18..a9d2321 100644 --- a/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs +++ b/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs @@ -1,6 +1,7 @@ using AngleSharp; using AngleSharp.Dom; using YaeBlog.Abstraction; +using YaeBlog.Extensions; using YaeBlog.Models; namespace YaeBlog.Processors; @@ -20,20 +21,21 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor ApplyGlobalCssStyles(document); BeatifyTable(document); + BeatifyList(document); + BeatifyInlineCode(document); return essay.WithNewHtmlContent(document.DocumentElement.OuterHtml); } private readonly Dictionary _globalCssStyles = new() { - { "pre", "p-4 bg-slate-300 rounded-sm overflow-x-auto" }, + { "pre", "p-4 bg-gray-100 rounded-sm overflow-x-auto" }, { "h2", "text-3xl font-bold py-4" }, { "h3", "text-2xl font-bold py-3" }, { "h4", "text-xl font-bold py-2" }, { "h5", "text-lg font-bold py-1" }, { "p", "p-2" }, { "img", "w-11/12 block mx-auto my-2 rounded-md shadow-md" }, - { "ul", "list-disc pl-2" } }; private void ApplyGlobalCssStyles(IDocument document) @@ -99,4 +101,45 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor } } } + + private static void BeatifyList(IDocument document) + { + foreach (IElement ulElement in from e in document.All + where e.LocalName == "ul" + select e) + { + // 首先给
    元素添加样式 + ulElement.ClassList.Add("list-disc ml-10"); + + + foreach (IElement liElement in from e in ulElement.Children + where e.LocalName == "li" + select e) + { + // 修改
  • 元素中的

    元素样式 + // 默认的p-2间距有点太宽了 + foreach (IElement pElement in from e in liElement.Children + where e.LocalName == "p" + select e) + { + pElement.ClassList.Remove("p-2"); + pElement.ClassList.Add("p-1"); + } + } + } + } + + private static void BeatifyInlineCode(IDocument document) + { + // 选择不在

    元素内的元素
    +        // 即行内代码
    +        IEnumerable inlineCodes = from e in document.All
    +            where e.LocalName == "code" && e.EnumerateParentElements().All(p => p.LocalName != "pre")
    +            select e;
    +
    +        foreach (IElement e in inlineCodes)
    +        {
    +            e.ClassList.Add("bg-gray-100 inline p-1 rounded-xs");
    +        }
    +    }
     }
    diff --git a/YaeBlog/YaeBlog.csproj b/YaeBlog/YaeBlog.csproj
    index 31b222f..546dc43 100644
    --- a/YaeBlog/YaeBlog.csproj
    +++ b/YaeBlog/YaeBlog.csproj
    @@ -13,7 +13,7 @@
         enable
       
     
    -  
    +  
         
         
           
    @@ -25,9 +25,13 @@
         
       
     
    -  
    +  
         
    -    
    +    
    +
    +    
    +      
    +    
       
     
     
    diff --git a/YaeBlog/package.json b/YaeBlog/package.json
    index 6a94197..1e0009f 100644
    --- a/YaeBlog/package.json
    +++ b/YaeBlog/package.json
    @@ -1,12 +1,15 @@
     {
    -  "name": "YaeBlog",
    -  "version": "1.0.0",
    -  "description": "",
    -  "scripts": {},
    -  "keywords": [],
    -  "author": "",
    -  "license": "ISC",
    -  "devDependencies": {
    -    "tailwindcss": "^3.4.16"
    -  }
    +    "name": "yae-blog",
    +    "version": "1.0.0",
    +    "description": "",
    +    "scripts": {
    +        "dev": "tailwindcss -i wwwroot/tailwind.css -o wwwroot/tailwind.g.css -w"
    +    },
    +    "keywords": [],
    +    "author": "",
    +    "license": "ISC",
    +    "devDependencies": {
    +        "tailwindcss": "^4.0.0",
    +        "@tailwindcss/cli": "^4.0.0"
    +    }
     }
    diff --git a/YaeBlog/pnpm-lock.yaml b/YaeBlog/pnpm-lock.yaml
    index dae0173..725fd8a 100644
    --- a/YaeBlog/pnpm-lock.yaml
    +++ b/YaeBlog/pnpm-lock.yaml
    @@ -8,192 +8,212 @@ importers:
     
       .:
         devDependencies:
    +      '@tailwindcss/cli':
    +        specifier: ^4.0.0
    +        version: 4.0.15
           tailwindcss:
    -        specifier: ^3.4.16
    -        version: 3.4.16
    +        specifier: ^4.0.0
    +        version: 4.0.15
     
     packages:
     
    -  '@alloc/quick-lru@5.2.0':
    -    resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
    -    engines: {node: '>=10'}
    +  '@parcel/watcher-android-arm64@2.5.1':
    +    resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [arm64]
    +    os: [android]
     
    -  '@isaacs/cliui@8.0.2':
    -    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
    -    engines: {node: '>=12'}
    +  '@parcel/watcher-darwin-arm64@2.5.1':
    +    resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [arm64]
    +    os: [darwin]
     
    -  '@jridgewell/gen-mapping@0.3.8':
    -    resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
    -    engines: {node: '>=6.0.0'}
    +  '@parcel/watcher-darwin-x64@2.5.1':
    +    resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [x64]
    +    os: [darwin]
     
    -  '@jridgewell/resolve-uri@3.1.2':
    -    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
    -    engines: {node: '>=6.0.0'}
    +  '@parcel/watcher-freebsd-x64@2.5.1':
    +    resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [x64]
    +    os: [freebsd]
     
    -  '@jridgewell/set-array@1.2.1':
    -    resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
    -    engines: {node: '>=6.0.0'}
    +  '@parcel/watcher-linux-arm-glibc@2.5.1':
    +    resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [arm]
    +    os: [linux]
    +    libc: [glibc]
     
    -  '@jridgewell/sourcemap-codec@1.5.0':
    -    resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
    +  '@parcel/watcher-linux-arm-musl@2.5.1':
    +    resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [arm]
    +    os: [linux]
    +    libc: [musl]
     
    -  '@jridgewell/trace-mapping@0.3.25':
    -    resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
    +  '@parcel/watcher-linux-arm64-glibc@2.5.1':
    +    resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [arm64]
    +    os: [linux]
    +    libc: [glibc]
     
    -  '@nodelib/fs.scandir@2.1.5':
    -    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
    -    engines: {node: '>= 8'}
    +  '@parcel/watcher-linux-arm64-musl@2.5.1':
    +    resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [arm64]
    +    os: [linux]
    +    libc: [musl]
     
    -  '@nodelib/fs.stat@2.0.5':
    -    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
    -    engines: {node: '>= 8'}
    +  '@parcel/watcher-linux-x64-glibc@2.5.1':
    +    resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [x64]
    +    os: [linux]
    +    libc: [glibc]
     
    -  '@nodelib/fs.walk@1.2.8':
    -    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
    -    engines: {node: '>= 8'}
    +  '@parcel/watcher-linux-x64-musl@2.5.1':
    +    resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [x64]
    +    os: [linux]
    +    libc: [musl]
     
    -  '@pkgjs/parseargs@0.11.0':
    -    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
    -    engines: {node: '>=14'}
    +  '@parcel/watcher-win32-arm64@2.5.1':
    +    resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [arm64]
    +    os: [win32]
     
    -  ansi-regex@5.0.1:
    -    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
    -    engines: {node: '>=8'}
    +  '@parcel/watcher-win32-ia32@2.5.1':
    +    resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [ia32]
    +    os: [win32]
     
    -  ansi-regex@6.1.0:
    -    resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
    -    engines: {node: '>=12'}
    +  '@parcel/watcher-win32-x64@2.5.1':
    +    resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
    +    engines: {node: '>= 10.0.0'}
    +    cpu: [x64]
    +    os: [win32]
     
    -  ansi-styles@4.3.0:
    -    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
    -    engines: {node: '>=8'}
    +  '@parcel/watcher@2.5.1':
    +    resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
    +    engines: {node: '>= 10.0.0'}
     
    -  ansi-styles@6.2.1:
    -    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
    -    engines: {node: '>=12'}
    +  '@tailwindcss/cli@4.0.15':
    +    resolution: {integrity: sha512-52RdNZCpij4O8+25N9sfWZPG124e6ahmIS1uMHcJrdw10UdpPUFgSJtyMwf7COVOnkx0nkXfmp8CcYomPCrQ1Q==}
    +    hasBin: true
     
    -  any-promise@1.3.0:
    -    resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
    +  '@tailwindcss/node@4.0.15':
    +    resolution: {integrity: sha512-IODaJjNmiasfZX3IoS+4Em3iu0fD2HS0/tgrnkYfW4hyUor01Smnr5eY3jc4rRgaTDrJlDmBTHbFO0ETTDaxWA==}
     
    -  anymatch@3.1.3:
    -    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
    -    engines: {node: '>= 8'}
    +  '@tailwindcss/oxide-android-arm64@4.0.15':
    +    resolution: {integrity: sha512-EBuyfSKkom7N+CB3A+7c0m4+qzKuiN0WCvzPvj5ZoRu4NlQadg/mthc1tl5k9b5ffRGsbDvP4k21azU4VwVk3Q==}
    +    engines: {node: '>= 10'}
    +    cpu: [arm64]
    +    os: [android]
     
    -  arg@5.0.2:
    -    resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
    +  '@tailwindcss/oxide-darwin-arm64@4.0.15':
    +    resolution: {integrity: sha512-ObVAnEpLepMhV9VoO0JSit66jiN5C4YCqW3TflsE9boo2Z7FIjV80RFbgeL2opBhtxbaNEDa6D0/hq/EP03kgQ==}
    +    engines: {node: '>= 10'}
    +    cpu: [arm64]
    +    os: [darwin]
     
    -  balanced-match@1.0.2:
    -    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
    +  '@tailwindcss/oxide-darwin-x64@4.0.15':
    +    resolution: {integrity: sha512-IElwoFhUinOr9MyKmGTPNi1Rwdh68JReFgYWibPWTGuevkHkLWKEflZc2jtI5lWZ5U9JjUnUfnY43I4fEXrc4g==}
    +    engines: {node: '>= 10'}
    +    cpu: [x64]
    +    os: [darwin]
     
    -  binary-extensions@2.3.0:
    -    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
    -    engines: {node: '>=8'}
    +  '@tailwindcss/oxide-freebsd-x64@4.0.15':
    +    resolution: {integrity: sha512-6BLLqyx7SIYRBOnTZ8wgfXANLJV5TQd3PevRJZp0vn42eO58A2LykRKdvL1qyPfdpmEVtF+uVOEZ4QTMqDRAWA==}
    +    engines: {node: '>= 10'}
    +    cpu: [x64]
    +    os: [freebsd]
     
    -  brace-expansion@2.0.1:
    -    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
    +  '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.15':
    +    resolution: {integrity: sha512-Zy63EVqO9241Pfg6G0IlRIWyY5vNcWrL5dd2WAKVJZRQVeolXEf1KfjkyeAAlErDj72cnyXObEZjMoPEKHpdNw==}
    +    engines: {node: '>= 10'}
    +    cpu: [arm]
    +    os: [linux]
    +
    +  '@tailwindcss/oxide-linux-arm64-gnu@4.0.15':
    +    resolution: {integrity: sha512-2NemGQeaTbtIp1Z2wyerbVEJZTkAWhMDOhhR5z/zJ75yMNf8yLnE+sAlyf6yGDNr+1RqvWrRhhCFt7i0CIxe4Q==}
    +    engines: {node: '>= 10'}
    +    cpu: [arm64]
    +    os: [linux]
    +    libc: [glibc]
    +
    +  '@tailwindcss/oxide-linux-arm64-musl@4.0.15':
    +    resolution: {integrity: sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==}
    +    engines: {node: '>= 10'}
    +    cpu: [arm64]
    +    os: [linux]
    +    libc: [musl]
    +
    +  '@tailwindcss/oxide-linux-x64-gnu@4.0.15':
    +    resolution: {integrity: sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==}
    +    engines: {node: '>= 10'}
    +    cpu: [x64]
    +    os: [linux]
    +    libc: [glibc]
    +
    +  '@tailwindcss/oxide-linux-x64-musl@4.0.15':
    +    resolution: {integrity: sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==}
    +    engines: {node: '>= 10'}
    +    cpu: [x64]
    +    os: [linux]
    +    libc: [musl]
    +
    +  '@tailwindcss/oxide-win32-arm64-msvc@4.0.15':
    +    resolution: {integrity: sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==}
    +    engines: {node: '>= 10'}
    +    cpu: [arm64]
    +    os: [win32]
    +
    +  '@tailwindcss/oxide-win32-x64-msvc@4.0.15':
    +    resolution: {integrity: sha512-JQ5H+5MLhOjpgNp6KomouE0ZuKmk3hO5h7/ClMNAQ8gZI2zkli3IH8ZqLbd2DVfXDbdxN2xvooIEeIlkIoSCqw==}
    +    engines: {node: '>= 10'}
    +    cpu: [x64]
    +    os: [win32]
    +
    +  '@tailwindcss/oxide@4.0.15':
    +    resolution: {integrity: sha512-e0uHrKfPu7JJGMfjwVNyt5M0u+OP8kUmhACwIRlM+JNBuReDVQ63yAD1NWe5DwJtdaHjugNBil76j+ks3zlk6g==}
    +    engines: {node: '>= 10'}
     
       braces@3.0.3:
         resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
         engines: {node: '>=8'}
     
    -  camelcase-css@2.0.1:
    -    resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
    -    engines: {node: '>= 6'}
    -
    -  chokidar@3.6.0:
    -    resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
    -    engines: {node: '>= 8.10.0'}
    -
    -  color-convert@2.0.1:
    -    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
    -    engines: {node: '>=7.0.0'}
    -
    -  color-name@1.1.4:
    -    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
    -
    -  commander@4.1.1:
    -    resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
    -    engines: {node: '>= 6'}
    -
    -  cross-spawn@7.0.6:
    -    resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
    -    engines: {node: '>= 8'}
    -
    -  cssesc@3.0.0:
    -    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
    -    engines: {node: '>=4'}
    +  detect-libc@1.0.3:
    +    resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
    +    engines: {node: '>=0.10'}
         hasBin: true
     
    -  didyoumean@1.2.2:
    -    resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
    +  detect-libc@2.0.3:
    +    resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
    +    engines: {node: '>=8'}
     
    -  dlv@1.1.3:
    -    resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
    -
    -  eastasianwidth@0.2.0:
    -    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
    -
    -  emoji-regex@8.0.0:
    -    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
    -
    -  emoji-regex@9.2.2:
    -    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
    -
    -  fast-glob@3.3.2:
    -    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
    -    engines: {node: '>=8.6.0'}
    -
    -  fastq@1.17.1:
    -    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
    +  enhanced-resolve@5.18.1:
    +    resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
    +    engines: {node: '>=10.13.0'}
     
       fill-range@7.1.1:
         resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
         engines: {node: '>=8'}
     
    -  foreground-child@3.3.0:
    -    resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
    -    engines: {node: '>=14'}
    -
    -  fsevents@2.3.3:
    -    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
    -    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
    -    os: [darwin]
    -
    -  function-bind@1.1.2:
    -    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
    -
    -  glob-parent@5.1.2:
    -    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
    -    engines: {node: '>= 6'}
    -
    -  glob-parent@6.0.2:
    -    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
    -    engines: {node: '>=10.13.0'}
    -
    -  glob@10.4.5:
    -    resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
    -    hasBin: true
    -
    -  hasown@2.0.2:
    -    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
    -    engines: {node: '>= 0.4'}
    -
    -  is-binary-path@2.1.0:
    -    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
    -    engines: {node: '>=8'}
    -
    -  is-core-module@2.16.0:
    -    resolution: {integrity: sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==}
    -    engines: {node: '>= 0.4'}
    +  graceful-fs@4.2.11:
    +    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
     
       is-extglob@2.1.1:
         resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
         engines: {node: '>=0.10.0'}
     
    -  is-fullwidth-code-point@3.0.0:
    -    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
    -    engines: {node: '>=8'}
    -
       is-glob@4.0.3:
         resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
         engines: {node: '>=0.10.0'}
    @@ -202,75 +222,88 @@ packages:
         resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
         engines: {node: '>=0.12.0'}
     
    -  isexe@2.0.0:
    -    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
    -
    -  jackspeak@3.4.3:
    -    resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
    -
    -  jiti@1.21.6:
    -    resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
    +  jiti@2.4.2:
    +    resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
         hasBin: true
     
    -  lilconfig@3.1.3:
    -    resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
    -    engines: {node: '>=14'}
    +  lightningcss-darwin-arm64@1.29.2:
    +    resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [arm64]
    +    os: [darwin]
     
    -  lines-and-columns@1.2.4:
    -    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
    +  lightningcss-darwin-x64@1.29.2:
    +    resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [x64]
    +    os: [darwin]
     
    -  lru-cache@10.4.3:
    -    resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
    +  lightningcss-freebsd-x64@1.29.2:
    +    resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [x64]
    +    os: [freebsd]
     
    -  merge2@1.4.1:
    -    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
    -    engines: {node: '>= 8'}
    +  lightningcss-linux-arm-gnueabihf@1.29.2:
    +    resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [arm]
    +    os: [linux]
    +
    +  lightningcss-linux-arm64-gnu@1.29.2:
    +    resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [arm64]
    +    os: [linux]
    +    libc: [glibc]
    +
    +  lightningcss-linux-arm64-musl@1.29.2:
    +    resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [arm64]
    +    os: [linux]
    +    libc: [musl]
    +
    +  lightningcss-linux-x64-gnu@1.29.2:
    +    resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [x64]
    +    os: [linux]
    +    libc: [glibc]
    +
    +  lightningcss-linux-x64-musl@1.29.2:
    +    resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [x64]
    +    os: [linux]
    +    libc: [musl]
    +
    +  lightningcss-win32-arm64-msvc@1.29.2:
    +    resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [arm64]
    +    os: [win32]
    +
    +  lightningcss-win32-x64-msvc@1.29.2:
    +    resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==}
    +    engines: {node: '>= 12.0.0'}
    +    cpu: [x64]
    +    os: [win32]
    +
    +  lightningcss@1.29.2:
    +    resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==}
    +    engines: {node: '>= 12.0.0'}
     
       micromatch@4.0.8:
         resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
         engines: {node: '>=8.6'}
     
    -  minimatch@9.0.5:
    -    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
    -    engines: {node: '>=16 || 14 >=14.17'}
    +  mri@1.2.0:
    +    resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
    +    engines: {node: '>=4'}
     
    -  minipass@7.1.2:
    -    resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
    -    engines: {node: '>=16 || 14 >=14.17'}
    -
    -  mz@2.7.0:
    -    resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
    -
    -  nanoid@3.3.8:
    -    resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
    -    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
    -    hasBin: true
    -
    -  normalize-path@3.0.0:
    -    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
    -    engines: {node: '>=0.10.0'}
    -
    -  object-assign@4.1.1:
    -    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
    -    engines: {node: '>=0.10.0'}
    -
    -  object-hash@3.0.0:
    -    resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
    -    engines: {node: '>= 6'}
    -
    -  package-json-from-dist@1.0.1:
    -    resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
    -
    -  path-key@3.1.1:
    -    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
    -    engines: {node: '>=8'}
    -
    -  path-parse@1.0.7:
    -    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
    -
    -  path-scurry@1.11.1:
    -    resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
    -    engines: {node: '>=16 || 14 >=14.18'}
    +  node-addon-api@7.1.1:
    +    resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
     
       picocolors@1.1.1:
         resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
    @@ -279,558 +312,234 @@ packages:
         resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
         engines: {node: '>=8.6'}
     
    -  pify@2.3.0:
    -    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
    -    engines: {node: '>=0.10.0'}
    +  tailwindcss@4.0.15:
    +    resolution: {integrity: sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==}
     
    -  pirates@4.0.6:
    -    resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
    -    engines: {node: '>= 6'}
    -
    -  postcss-import@15.1.0:
    -    resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
    -    engines: {node: '>=14.0.0'}
    -    peerDependencies:
    -      postcss: ^8.0.0
    -
    -  postcss-js@4.0.1:
    -    resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
    -    engines: {node: ^12 || ^14 || >= 16}
    -    peerDependencies:
    -      postcss: ^8.4.21
    -
    -  postcss-load-config@4.0.2:
    -    resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
    -    engines: {node: '>= 14'}
    -    peerDependencies:
    -      postcss: '>=8.0.9'
    -      ts-node: '>=9.0.0'
    -    peerDependenciesMeta:
    -      postcss:
    -        optional: true
    -      ts-node:
    -        optional: true
    -
    -  postcss-nested@6.2.0:
    -    resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
    -    engines: {node: '>=12.0'}
    -    peerDependencies:
    -      postcss: ^8.2.14
    -
    -  postcss-selector-parser@6.1.2:
    -    resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
    -    engines: {node: '>=4'}
    -
    -  postcss-value-parser@4.2.0:
    -    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
    -
    -  postcss@8.4.49:
    -    resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
    -    engines: {node: ^10 || ^12 || >=14}
    -
    -  queue-microtask@1.2.3:
    -    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
    -
    -  read-cache@1.0.0:
    -    resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
    -
    -  readdirp@3.6.0:
    -    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
    -    engines: {node: '>=8.10.0'}
    -
    -  resolve@1.22.9:
    -    resolution: {integrity: sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==}
    -    hasBin: true
    -
    -  reusify@1.0.4:
    -    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
    -    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
    -
    -  run-parallel@1.2.0:
    -    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
    -
    -  shebang-command@2.0.0:
    -    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
    -    engines: {node: '>=8'}
    -
    -  shebang-regex@3.0.0:
    -    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
    -    engines: {node: '>=8'}
    -
    -  signal-exit@4.1.0:
    -    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
    -    engines: {node: '>=14'}
    -
    -  source-map-js@1.2.1:
    -    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
    -    engines: {node: '>=0.10.0'}
    -
    -  string-width@4.2.3:
    -    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
    -    engines: {node: '>=8'}
    -
    -  string-width@5.1.2:
    -    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
    -    engines: {node: '>=12'}
    -
    -  strip-ansi@6.0.1:
    -    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
    -    engines: {node: '>=8'}
    -
    -  strip-ansi@7.1.0:
    -    resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
    -    engines: {node: '>=12'}
    -
    -  sucrase@3.35.0:
    -    resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
    -    engines: {node: '>=16 || 14 >=14.17'}
    -    hasBin: true
    -
    -  supports-preserve-symlinks-flag@1.0.0:
    -    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
    -    engines: {node: '>= 0.4'}
    -
    -  tailwindcss@3.4.16:
    -    resolution: {integrity: sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==}
    -    engines: {node: '>=14.0.0'}
    -    hasBin: true
    -
    -  thenify-all@1.6.0:
    -    resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
    -    engines: {node: '>=0.8'}
    -
    -  thenify@3.3.1:
    -    resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
    +  tapable@2.2.1:
    +    resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
    +    engines: {node: '>=6'}
     
       to-regex-range@5.0.1:
         resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
         engines: {node: '>=8.0'}
     
    -  ts-interface-checker@0.1.13:
    -    resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
    -
    -  util-deprecate@1.0.2:
    -    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
    -
    -  which@2.0.2:
    -    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
    -    engines: {node: '>= 8'}
    -    hasBin: true
    -
    -  wrap-ansi@7.0.0:
    -    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
    -    engines: {node: '>=10'}
    -
    -  wrap-ansi@8.1.0:
    -    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
    -    engines: {node: '>=12'}
    -
    -  yaml@2.6.1:
    -    resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==}
    -    engines: {node: '>= 14'}
    -    hasBin: true
    -
     snapshots:
     
    -  '@alloc/quick-lru@5.2.0': {}
    -
    -  '@isaacs/cliui@8.0.2':
    -    dependencies:
    -      string-width: 5.1.2
    -      string-width-cjs: string-width@4.2.3
    -      strip-ansi: 7.1.0
    -      strip-ansi-cjs: strip-ansi@6.0.1
    -      wrap-ansi: 8.1.0
    -      wrap-ansi-cjs: wrap-ansi@7.0.0
    -
    -  '@jridgewell/gen-mapping@0.3.8':
    -    dependencies:
    -      '@jridgewell/set-array': 1.2.1
    -      '@jridgewell/sourcemap-codec': 1.5.0
    -      '@jridgewell/trace-mapping': 0.3.25
    -
    -  '@jridgewell/resolve-uri@3.1.2': {}
    -
    -  '@jridgewell/set-array@1.2.1': {}
    -
    -  '@jridgewell/sourcemap-codec@1.5.0': {}
    -
    -  '@jridgewell/trace-mapping@0.3.25':
    -    dependencies:
    -      '@jridgewell/resolve-uri': 3.1.2
    -      '@jridgewell/sourcemap-codec': 1.5.0
    -
    -  '@nodelib/fs.scandir@2.1.5':
    -    dependencies:
    -      '@nodelib/fs.stat': 2.0.5
    -      run-parallel: 1.2.0
    -
    -  '@nodelib/fs.stat@2.0.5': {}
    -
    -  '@nodelib/fs.walk@1.2.8':
    -    dependencies:
    -      '@nodelib/fs.scandir': 2.1.5
    -      fastq: 1.17.1
    -
    -  '@pkgjs/parseargs@0.11.0':
    +  '@parcel/watcher-android-arm64@2.5.1':
         optional: true
     
    -  ansi-regex@5.0.1: {}
    +  '@parcel/watcher-darwin-arm64@2.5.1':
    +    optional: true
     
    -  ansi-regex@6.1.0: {}
    +  '@parcel/watcher-darwin-x64@2.5.1':
    +    optional: true
     
    -  ansi-styles@4.3.0:
    +  '@parcel/watcher-freebsd-x64@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher-linux-arm-glibc@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher-linux-arm-musl@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher-linux-arm64-glibc@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher-linux-arm64-musl@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher-linux-x64-glibc@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher-linux-x64-musl@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher-win32-arm64@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher-win32-ia32@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher-win32-x64@2.5.1':
    +    optional: true
    +
    +  '@parcel/watcher@2.5.1':
         dependencies:
    -      color-convert: 2.0.1
    +      detect-libc: 1.0.3
    +      is-glob: 4.0.3
    +      micromatch: 4.0.8
    +      node-addon-api: 7.1.1
    +    optionalDependencies:
    +      '@parcel/watcher-android-arm64': 2.5.1
    +      '@parcel/watcher-darwin-arm64': 2.5.1
    +      '@parcel/watcher-darwin-x64': 2.5.1
    +      '@parcel/watcher-freebsd-x64': 2.5.1
    +      '@parcel/watcher-linux-arm-glibc': 2.5.1
    +      '@parcel/watcher-linux-arm-musl': 2.5.1
    +      '@parcel/watcher-linux-arm64-glibc': 2.5.1
    +      '@parcel/watcher-linux-arm64-musl': 2.5.1
    +      '@parcel/watcher-linux-x64-glibc': 2.5.1
    +      '@parcel/watcher-linux-x64-musl': 2.5.1
    +      '@parcel/watcher-win32-arm64': 2.5.1
    +      '@parcel/watcher-win32-ia32': 2.5.1
    +      '@parcel/watcher-win32-x64': 2.5.1
     
    -  ansi-styles@6.2.1: {}
    -
    -  any-promise@1.3.0: {}
    -
    -  anymatch@3.1.3:
    +  '@tailwindcss/cli@4.0.15':
         dependencies:
    -      normalize-path: 3.0.0
    -      picomatch: 2.3.1
    +      '@parcel/watcher': 2.5.1
    +      '@tailwindcss/node': 4.0.15
    +      '@tailwindcss/oxide': 4.0.15
    +      enhanced-resolve: 5.18.1
    +      lightningcss: 1.29.2
    +      mri: 1.2.0
    +      picocolors: 1.1.1
    +      tailwindcss: 4.0.15
     
    -  arg@5.0.2: {}
    -
    -  balanced-match@1.0.2: {}
    -
    -  binary-extensions@2.3.0: {}
    -
    -  brace-expansion@2.0.1:
    +  '@tailwindcss/node@4.0.15':
         dependencies:
    -      balanced-match: 1.0.2
    +      enhanced-resolve: 5.18.1
    +      jiti: 2.4.2
    +      tailwindcss: 4.0.15
    +
    +  '@tailwindcss/oxide-android-arm64@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-darwin-arm64@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-darwin-x64@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-freebsd-x64@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-linux-arm64-gnu@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-linux-arm64-musl@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-linux-x64-gnu@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-linux-x64-musl@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-win32-arm64-msvc@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide-win32-x64-msvc@4.0.15':
    +    optional: true
    +
    +  '@tailwindcss/oxide@4.0.15':
    +    optionalDependencies:
    +      '@tailwindcss/oxide-android-arm64': 4.0.15
    +      '@tailwindcss/oxide-darwin-arm64': 4.0.15
    +      '@tailwindcss/oxide-darwin-x64': 4.0.15
    +      '@tailwindcss/oxide-freebsd-x64': 4.0.15
    +      '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.15
    +      '@tailwindcss/oxide-linux-arm64-gnu': 4.0.15
    +      '@tailwindcss/oxide-linux-arm64-musl': 4.0.15
    +      '@tailwindcss/oxide-linux-x64-gnu': 4.0.15
    +      '@tailwindcss/oxide-linux-x64-musl': 4.0.15
    +      '@tailwindcss/oxide-win32-arm64-msvc': 4.0.15
    +      '@tailwindcss/oxide-win32-x64-msvc': 4.0.15
     
       braces@3.0.3:
         dependencies:
           fill-range: 7.1.1
     
    -  camelcase-css@2.0.1: {}
    +  detect-libc@1.0.3: {}
     
    -  chokidar@3.6.0:
    +  detect-libc@2.0.3: {}
    +
    +  enhanced-resolve@5.18.1:
         dependencies:
    -      anymatch: 3.1.3
    -      braces: 3.0.3
    -      glob-parent: 5.1.2
    -      is-binary-path: 2.1.0
    -      is-glob: 4.0.3
    -      normalize-path: 3.0.0
    -      readdirp: 3.6.0
    -    optionalDependencies:
    -      fsevents: 2.3.3
    -
    -  color-convert@2.0.1:
    -    dependencies:
    -      color-name: 1.1.4
    -
    -  color-name@1.1.4: {}
    -
    -  commander@4.1.1: {}
    -
    -  cross-spawn@7.0.6:
    -    dependencies:
    -      path-key: 3.1.1
    -      shebang-command: 2.0.0
    -      which: 2.0.2
    -
    -  cssesc@3.0.0: {}
    -
    -  didyoumean@1.2.2: {}
    -
    -  dlv@1.1.3: {}
    -
    -  eastasianwidth@0.2.0: {}
    -
    -  emoji-regex@8.0.0: {}
    -
    -  emoji-regex@9.2.2: {}
    -
    -  fast-glob@3.3.2:
    -    dependencies:
    -      '@nodelib/fs.stat': 2.0.5
    -      '@nodelib/fs.walk': 1.2.8
    -      glob-parent: 5.1.2
    -      merge2: 1.4.1
    -      micromatch: 4.0.8
    -
    -  fastq@1.17.1:
    -    dependencies:
    -      reusify: 1.0.4
    +      graceful-fs: 4.2.11
    +      tapable: 2.2.1
     
       fill-range@7.1.1:
         dependencies:
           to-regex-range: 5.0.1
     
    -  foreground-child@3.3.0:
    -    dependencies:
    -      cross-spawn: 7.0.6
    -      signal-exit: 4.1.0
    -
    -  fsevents@2.3.3:
    -    optional: true
    -
    -  function-bind@1.1.2: {}
    -
    -  glob-parent@5.1.2:
    -    dependencies:
    -      is-glob: 4.0.3
    -
    -  glob-parent@6.0.2:
    -    dependencies:
    -      is-glob: 4.0.3
    -
    -  glob@10.4.5:
    -    dependencies:
    -      foreground-child: 3.3.0
    -      jackspeak: 3.4.3
    -      minimatch: 9.0.5
    -      minipass: 7.1.2
    -      package-json-from-dist: 1.0.1
    -      path-scurry: 1.11.1
    -
    -  hasown@2.0.2:
    -    dependencies:
    -      function-bind: 1.1.2
    -
    -  is-binary-path@2.1.0:
    -    dependencies:
    -      binary-extensions: 2.3.0
    -
    -  is-core-module@2.16.0:
    -    dependencies:
    -      hasown: 2.0.2
    +  graceful-fs@4.2.11: {}
     
       is-extglob@2.1.1: {}
     
    -  is-fullwidth-code-point@3.0.0: {}
    -
       is-glob@4.0.3:
         dependencies:
           is-extglob: 2.1.1
     
       is-number@7.0.0: {}
     
    -  isexe@2.0.0: {}
    +  jiti@2.4.2: {}
     
    -  jackspeak@3.4.3:
    +  lightningcss-darwin-arm64@1.29.2:
    +    optional: true
    +
    +  lightningcss-darwin-x64@1.29.2:
    +    optional: true
    +
    +  lightningcss-freebsd-x64@1.29.2:
    +    optional: true
    +
    +  lightningcss-linux-arm-gnueabihf@1.29.2:
    +    optional: true
    +
    +  lightningcss-linux-arm64-gnu@1.29.2:
    +    optional: true
    +
    +  lightningcss-linux-arm64-musl@1.29.2:
    +    optional: true
    +
    +  lightningcss-linux-x64-gnu@1.29.2:
    +    optional: true
    +
    +  lightningcss-linux-x64-musl@1.29.2:
    +    optional: true
    +
    +  lightningcss-win32-arm64-msvc@1.29.2:
    +    optional: true
    +
    +  lightningcss-win32-x64-msvc@1.29.2:
    +    optional: true
    +
    +  lightningcss@1.29.2:
         dependencies:
    -      '@isaacs/cliui': 8.0.2
    +      detect-libc: 2.0.3
         optionalDependencies:
    -      '@pkgjs/parseargs': 0.11.0
    -
    -  jiti@1.21.6: {}
    -
    -  lilconfig@3.1.3: {}
    -
    -  lines-and-columns@1.2.4: {}
    -
    -  lru-cache@10.4.3: {}
    -
    -  merge2@1.4.1: {}
    +      lightningcss-darwin-arm64: 1.29.2
    +      lightningcss-darwin-x64: 1.29.2
    +      lightningcss-freebsd-x64: 1.29.2
    +      lightningcss-linux-arm-gnueabihf: 1.29.2
    +      lightningcss-linux-arm64-gnu: 1.29.2
    +      lightningcss-linux-arm64-musl: 1.29.2
    +      lightningcss-linux-x64-gnu: 1.29.2
    +      lightningcss-linux-x64-musl: 1.29.2
    +      lightningcss-win32-arm64-msvc: 1.29.2
    +      lightningcss-win32-x64-msvc: 1.29.2
     
       micromatch@4.0.8:
         dependencies:
           braces: 3.0.3
           picomatch: 2.3.1
     
    -  minimatch@9.0.5:
    -    dependencies:
    -      brace-expansion: 2.0.1
    +  mri@1.2.0: {}
     
    -  minipass@7.1.2: {}
    -
    -  mz@2.7.0:
    -    dependencies:
    -      any-promise: 1.3.0
    -      object-assign: 4.1.1
    -      thenify-all: 1.6.0
    -
    -  nanoid@3.3.8: {}
    -
    -  normalize-path@3.0.0: {}
    -
    -  object-assign@4.1.1: {}
    -
    -  object-hash@3.0.0: {}
    -
    -  package-json-from-dist@1.0.1: {}
    -
    -  path-key@3.1.1: {}
    -
    -  path-parse@1.0.7: {}
    -
    -  path-scurry@1.11.1:
    -    dependencies:
    -      lru-cache: 10.4.3
    -      minipass: 7.1.2
    +  node-addon-api@7.1.1: {}
     
       picocolors@1.1.1: {}
     
       picomatch@2.3.1: {}
     
    -  pify@2.3.0: {}
    +  tailwindcss@4.0.15: {}
     
    -  pirates@4.0.6: {}
    -
    -  postcss-import@15.1.0(postcss@8.4.49):
    -    dependencies:
    -      postcss: 8.4.49
    -      postcss-value-parser: 4.2.0
    -      read-cache: 1.0.0
    -      resolve: 1.22.9
    -
    -  postcss-js@4.0.1(postcss@8.4.49):
    -    dependencies:
    -      camelcase-css: 2.0.1
    -      postcss: 8.4.49
    -
    -  postcss-load-config@4.0.2(postcss@8.4.49):
    -    dependencies:
    -      lilconfig: 3.1.3
    -      yaml: 2.6.1
    -    optionalDependencies:
    -      postcss: 8.4.49
    -
    -  postcss-nested@6.2.0(postcss@8.4.49):
    -    dependencies:
    -      postcss: 8.4.49
    -      postcss-selector-parser: 6.1.2
    -
    -  postcss-selector-parser@6.1.2:
    -    dependencies:
    -      cssesc: 3.0.0
    -      util-deprecate: 1.0.2
    -
    -  postcss-value-parser@4.2.0: {}
    -
    -  postcss@8.4.49:
    -    dependencies:
    -      nanoid: 3.3.8
    -      picocolors: 1.1.1
    -      source-map-js: 1.2.1
    -
    -  queue-microtask@1.2.3: {}
    -
    -  read-cache@1.0.0:
    -    dependencies:
    -      pify: 2.3.0
    -
    -  readdirp@3.6.0:
    -    dependencies:
    -      picomatch: 2.3.1
    -
    -  resolve@1.22.9:
    -    dependencies:
    -      is-core-module: 2.16.0
    -      path-parse: 1.0.7
    -      supports-preserve-symlinks-flag: 1.0.0
    -
    -  reusify@1.0.4: {}
    -
    -  run-parallel@1.2.0:
    -    dependencies:
    -      queue-microtask: 1.2.3
    -
    -  shebang-command@2.0.0:
    -    dependencies:
    -      shebang-regex: 3.0.0
    -
    -  shebang-regex@3.0.0: {}
    -
    -  signal-exit@4.1.0: {}
    -
    -  source-map-js@1.2.1: {}
    -
    -  string-width@4.2.3:
    -    dependencies:
    -      emoji-regex: 8.0.0
    -      is-fullwidth-code-point: 3.0.0
    -      strip-ansi: 6.0.1
    -
    -  string-width@5.1.2:
    -    dependencies:
    -      eastasianwidth: 0.2.0
    -      emoji-regex: 9.2.2
    -      strip-ansi: 7.1.0
    -
    -  strip-ansi@6.0.1:
    -    dependencies:
    -      ansi-regex: 5.0.1
    -
    -  strip-ansi@7.1.0:
    -    dependencies:
    -      ansi-regex: 6.1.0
    -
    -  sucrase@3.35.0:
    -    dependencies:
    -      '@jridgewell/gen-mapping': 0.3.8
    -      commander: 4.1.1
    -      glob: 10.4.5
    -      lines-and-columns: 1.2.4
    -      mz: 2.7.0
    -      pirates: 4.0.6
    -      ts-interface-checker: 0.1.13
    -
    -  supports-preserve-symlinks-flag@1.0.0: {}
    -
    -  tailwindcss@3.4.16:
    -    dependencies:
    -      '@alloc/quick-lru': 5.2.0
    -      arg: 5.0.2
    -      chokidar: 3.6.0
    -      didyoumean: 1.2.2
    -      dlv: 1.1.3
    -      fast-glob: 3.3.2
    -      glob-parent: 6.0.2
    -      is-glob: 4.0.3
    -      jiti: 1.21.6
    -      lilconfig: 3.1.3
    -      micromatch: 4.0.8
    -      normalize-path: 3.0.0
    -      object-hash: 3.0.0
    -      picocolors: 1.1.1
    -      postcss: 8.4.49
    -      postcss-import: 15.1.0(postcss@8.4.49)
    -      postcss-js: 4.0.1(postcss@8.4.49)
    -      postcss-load-config: 4.0.2(postcss@8.4.49)
    -      postcss-nested: 6.2.0(postcss@8.4.49)
    -      postcss-selector-parser: 6.1.2
    -      resolve: 1.22.9
    -      sucrase: 3.35.0
    -    transitivePeerDependencies:
    -      - ts-node
    -
    -  thenify-all@1.6.0:
    -    dependencies:
    -      thenify: 3.3.1
    -
    -  thenify@3.3.1:
    -    dependencies:
    -      any-promise: 1.3.0
    +  tapable@2.2.1: {}
     
       to-regex-range@5.0.1:
         dependencies:
           is-number: 7.0.0
    -
    -  ts-interface-checker@0.1.13: {}
    -
    -  util-deprecate@1.0.2: {}
    -
    -  which@2.0.2:
    -    dependencies:
    -      isexe: 2.0.0
    -
    -  wrap-ansi@7.0.0:
    -    dependencies:
    -      ansi-styles: 4.3.0
    -      string-width: 4.2.3
    -      strip-ansi: 6.0.1
    -
    -  wrap-ansi@8.1.0:
    -    dependencies:
    -      ansi-styles: 6.2.1
    -      string-width: 5.1.2
    -      strip-ansi: 7.1.0
    -
    -  yaml@2.6.1: {}
    diff --git a/YaeBlog/tailwind.config.js b/YaeBlog/tailwind.config.js
    deleted file mode 100644
    index 36dd647..0000000
    --- a/YaeBlog/tailwind.config.js
    +++ /dev/null
    @@ -1,9 +0,0 @@
    -/** @type {import('tailwindcss').Config} */
    -module.exports = {
    -  content: ["**/*.razor", "**/*.cshtml", "**/*.html", "Processors/EssayStylesPostRenderProcessor.cs"],
    -  theme: {
    -    extend: {},
    -  },
    -  plugins: [],
    -}
    -
    diff --git a/YaeBlog/wwwroot/fonts/bootstrap-icons.woff b/YaeBlog/wwwroot/fonts/bootstrap-icons.woff
    deleted file mode 100644
    index 51204d27de92c7bb0f8bed6165b9dc888f38ff38..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 176032
    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
    
    diff --git a/YaeBlog/wwwroot/fonts/bootstrap-icons.woff2 b/YaeBlog/wwwroot/fonts/bootstrap-icons.woff2
    deleted file mode 100644
    index cf957a1..0000000
    --- a/YaeBlog/wwwroot/fonts/bootstrap-icons.woff2
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:476adf42b40325098fcfa8b36ab3e769186bb4f6ce6a249753e2e1a9c22bf99e
    -size 130396
    diff --git a/YaeBlog/wwwroot/input.css b/YaeBlog/wwwroot/input.css
    deleted file mode 100644
    index b5c61c9..0000000
    --- a/YaeBlog/wwwroot/input.css
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -@tailwind base;
    -@tailwind components;
    -@tailwind utilities;
    diff --git a/YaeBlog/wwwroot/tailwind.css b/YaeBlog/wwwroot/tailwind.css
    new file mode 100644
    index 0000000..f1d8c73
    --- /dev/null
    +++ b/YaeBlog/wwwroot/tailwind.css
    @@ -0,0 +1 @@
    +@import "tailwindcss";
    
    From 3126005731f7e3157e31e83198c2d024ca09ead2 Mon Sep 17 00:00:00 2001
    From: jackfiled 
    Date: Tue, 25 Mar 2025 15:00:18 +0800
    Subject: [PATCH 15/41] =?UTF-8?q?feat:=20=E5=9B=BE=E7=89=87=E5=8E=8B?=
     =?UTF-8?q?=E7=BC=A9=E5=91=BD=E4=BB=A4=20(#10)?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    将图片压缩为webp格式减少流量使用和磁盘占用
    Reviewed-on: https://git.rrricardo.top/jackfiled/YaeBlog/pulls/10
    ---
     .gitattributes                                |   2 +
     YaeBlog/Abstraction/IEssayScanService.cs      |   2 -
     .../Binders/ImageCompressServiceBinder.cs     |  21 ++
     YaeBlog/Commands/YaeBlogCommand.cs            |  80 +++++--
     YaeBlog/Exceptions/BlogCommandException.cs    |  12 +
     YaeBlog/Models/BlogContent.cs                 |  24 +-
     YaeBlog/Models/BlogContents.cs                |  13 +-
     YaeBlog/Models/BlogImageInfo.cs               |  44 ++++
     YaeBlog/Models/ImageScanResult.cs             |   3 -
     YaeBlog/Services/EssayScanService.cs          | 214 ++++++++++--------
     YaeBlog/Services/ImageCompressService.cs      | 119 ++++++++++
     YaeBlog/Services/RendererService.cs           |  18 +-
     YaeBlog/YaeBlog.csproj                        |   2 +
     YaeBlog/source/posts/2021-final.md            |   9 +-
     YaeBlog/source/posts/2021-final/1.png         |   3 -
     YaeBlog/source/posts/2021-final/1.webp        |   3 +
     YaeBlog/source/posts/2022-final.md            |  11 +-
     ...-12-30-14-26-19-QQ_Image_1672381538441.jpg |   3 -
     ...12-30-14-26-19-QQ_Image_1672381538441.webp |   3 +
     ...-12-30-14-28-12-QQ_Image_1672381543836.jpg |   3 -
     ...12-30-14-28-12-QQ_Image_1672381543836.webp |   3 +
     YaeBlog/source/posts/2022-summer-vacation.md  |   8 +-
     .../posts/2022-summer-vacation/result1.png    |   3 -
     .../posts/2022-summer-vacation/result1.webp   |   3 +
     YaeBlog/source/posts/2023-final.md            |   9 +-
     .../2023-final/image-20240303165826486.png    |   3 -
     .../2023-final/image-20240303165826486.webp   |   3 +
     YaeBlog/source/posts/2024-final.md            |   3 +-
     .../2024-final/image-20250115171809775.png    |   3 -
     .../2024-final/image-20250115171809775.webp   |   3 +
     YaeBlog/source/posts/big-homework.md          |   8 +-
     YaeBlog/source/posts/big-homework/1.png       |   3 -
     YaeBlog/source/posts/big-homework/1.webp      |   3 +
     YaeBlog/source/posts/build-blog-record.md     |  10 +-
     YaeBlog/source/posts/build-blog-record/1.png  |   3 -
     YaeBlog/source/posts/build-blog-record/1.webp |   3 +
     YaeBlog/source/posts/build-blog-record/2.png  |   3 -
     YaeBlog/source/posts/build-blog-record/2.webp |   3 +
     .../source/posts/build-dotnet-from-source.md  |  11 +-
     .../image-20240824120646587.png               |   3 -
     .../image-20240824120646587.webp              |   3 +
     .../image-20240824121425007.png               |   3 -
     .../image-20240824121425007.webp              |   3 +
     .../image-20240824134158262.png               |   3 -
     .../image-20240824134158262.webp              |   3 +
     .../image-20240824153514149.png               |   3 -
     .../image-20240824153514149.webp              |   3 +
     .../image-20240824214145759.png               |   3 -
     .../image-20240824214145759.webp              |   3 +
     YaeBlog/source/posts/c-include-problems.md    |  12 +-
     YaeBlog/source/posts/c-include-problems/1.png |   3 -
     .../source/posts/c-include-problems/1.webp    |   3 +
     YaeBlog/source/posts/c-include-problems/2.png |   3 -
     .../source/posts/c-include-problems/2.webp    |   3 +
     YaeBlog/source/posts/cncc-2024.md             |   9 +-
     .../cncc-2024/image-20241102211959206.png     |   3 -
     .../cncc-2024/image-20241102211959206.webp    |   3 +
     .../cncc-2024/image-20241102212355390.png     |   3 -
     .../cncc-2024/image-20241102212355390.webp    |   3 +
     .../cncc-2024/image-20241102212536635.png     |   3 -
     .../cncc-2024/image-20241102212536635.webp    |   3 +
     .../cncc-2024/image-20241102212738598.png     |   3 -
     .../cncc-2024/image-20241102212738598.webp    |   3 +
     YaeBlog/source/posts/compile-mediapipe.md     |  11 +-
     ...15-22-05-41-Screenshot_20230115_220521.png |   3 -
     ...5-22-05-41-Screenshot_20230115_220521.webp |   3 +
     ...19-20-20-40-Screenshot_20230119_202008.png |   3 -
     ...9-20-20-40-Screenshot_20230119_202008.webp |   3 +
     .../posts/computer-architecture-pipeline.md   |  17 +-
     .../image-20240612184855300.png               |   3 -
     .../image-20240612184855300.webp              |   3 +
     .../image-20240612184949777.png               |   3 -
     .../image-20240612184949777.webp              |   3 +
     .../image-20240612190426368.png               |   3 -
     .../image-20240612190426368.webp              |   3 +
     .../image-20240612192700169.png               |   3 -
     .../image-20240612192700169.webp              |   3 +
     .../image-20240612193301372.png               |   3 -
     .../image-20240612193301372.webp              |   3 +
     YaeBlog/source/posts/daily-linux-0.md         |  12 +-
     ...12-13-28-38-Screenshot_20230112_132829.png |   3 -
     ...2-13-28-38-Screenshot_20230112_132829.webp |   3 +
     ...12-13-36-45-Screenshot_20230112_133628.png |   3 -
     ...2-13-36-45-Screenshot_20230112_133628.webp |   3 +
     YaeBlog/source/posts/daily-linux-2.md         |  12 +-
     ...11f6be2724b3b4725f7ce5a4078818844857.avif} |   0
     .../daily-linux-2/image-20230702205919301.png |   3 -
     .../image-20230702205919301.webp              |   3 +
     YaeBlog/source/posts/daily-linux-3.md         |  10 +-
     .../Screenshot_20230904_144149.png            |   3 -
     .../Screenshot_20230904_144149.webp           |   3 +
     YaeBlog/source/posts/daily-linux-4.md         |  17 +-
     .../Screenshot_20240309_115143.png            |   3 -
     .../Screenshot_20240309_115143.webp           |   3 +
     ...01a8e8c69fecf247f17fc1-1709963611271-2.jpg |   3 -
     ...1a8e8c69fecf247f17fc1-1709963611271-2.webp |   3 +
     .../daily-linux-4/image-20240309130329784.png |   3 -
     .../image-20240309130329784.webp              |   3 +
     .../daily-linux-4/image-20240309131750535.png |   3 -
     .../image-20240309131750535.webp              |   3 +
     .../daily-linux-4/image-20240309134847166.png |   3 -
     .../image-20240309134847166.webp              |   3 +
     YaeBlog/source/posts/dotnet-performance-8.md  |   7 +-
     .../HeapsWhereNetObjectsLive.png              |   3 -
     .../HeapsWhereNetObjectsLive.webp             |   3 +
     .../image-20240828135354598.png               |   3 -
     .../image-20240828135354598.webp              |   3 +
     .../image-20240828155556375.png               |   3 -
     .../image-20240828155556375.webp              |   3 +
     YaeBlog/source/posts/environment-setting.md   |   8 +-
     .../source/posts/environment-setting/6.png    |   3 -
     .../source/posts/environment-setting/6.webp   |   3 +
     YaeBlog/source/posts/genshin-gacha-1.md       |  21 +-
     .../2022-12-31-13-06-36-image.png             |   3 -
     .../2022-12-31-13-06-36-image.webp            |   3 +
     .../2022-12-31-13-20-46-image.png             |   3 -
     .../2022-12-31-13-20-46-image.webp            |   3 +
     .../2022-12-31-13-21-11-image.png             |   3 -
     .../2022-12-31-13-21-11-image.webp            |   3 +
     .../2022-12-31-13-24-26-image.png             |   3 -
     .../2022-12-31-13-24-26-image.webp            |   3 +
     .../2022-12-31-15-59-20-image.png             |   3 -
     .../2022-12-31-15-59-20-image.webp            |   3 +
     .../2022-12-31-15-59-42-image.png             |   3 -
     .../2022-12-31-15-59-42-image.webp            |   3 +
     .../2022-12-31-16-00-10-image.png             |   3 -
     .../2022-12-31-16-00-10-image.webp            |   3 +
     .../posts/heterogeneous-programming-model.md  |  21 +-
     .../83ee1d254d638536d0fb4197ff63e758.png      |   3 -
     .../83ee1d254d638536d0fb4197ff63e758.webp     |   3 +
     .../9eb06d8be92ddef3db33e040163c67a7.png      |   3 -
     .../9eb06d8be92ddef3db33e040163c67a7.webp     |   3 +
     .../Screenshot_20241016_214139.png            |   3 -
     .../Screenshot_20241016_214139.webp           |   3 +
     .../Screenshot_20241016_214939.png            |   3 -
     .../Screenshot_20241016_214939.webp           |   3 +
     .../eab553f9e30d8d866a1ddd201b5e4c85.png      |   3 -
     .../eab553f9e30d8d866a1ddd201b5e4c85.webp     |   3 +
     .../image-20241020142938110.png               |   3 -
     .../image-20241020142938110.webp              |   3 +
     .../image-20241020155656219.png               |   3 -
     .../image-20241020155656219.webp              |   3 +
     .../image-20241029123308139.png               |   3 -
     .../image-20241029123308139.webp              |   3 +
     .../image-20241029163654675.png               |   3 -
     .../image-20241029163654675.webp              |   3 +
     .../image-20241103162259981.png               |   3 -
     .../image-20241103162259981.webp              |   3 +
     .../source/posts/hpc-2025-cpu-architecture.md |   7 +-
     .../image-20250313184421305.png               |   3 -
     .../image-20250313184421305.webp              |   3 +
     .../image-20250313184732892.png               |   3 -
     .../image-20250313184732892.webp              |   3 +
     .../image-20250313190913475.png               |   3 -
     .../image-20250313190913475.webp              |   3 +
     YaeBlog/source/posts/install-pytorch.md       |  17 +-
     YaeBlog/source/posts/install-pytorch/1.png    |   3 -
     YaeBlog/source/posts/install-pytorch/1.webp   |   3 +
     YaeBlog/source/posts/install-pytorch/2.png    |   3 -
     YaeBlog/source/posts/install-pytorch/2.webp   |   3 +
     YaeBlog/source/posts/install-pytorch/3.png    |   3 -
     YaeBlog/source/posts/install-pytorch/3.webp   |   3 +
     YaeBlog/source/posts/install-pytorch/4.png    |   3 -
     YaeBlog/source/posts/install-pytorch/4.webp   |   3 +
     YaeBlog/source/posts/laptop-for-computer.md   |  14 +-
     .../source/posts/laptop-for-computer/c.png    |   3 -
     .../source/posts/laptop-for-computer/c.webp   |   3 +
     .../posts/laptop-for-computer/clion.png       |   3 -
     .../posts/laptop-for-computer/clion.webp      |   3 +
     .../posts/laptop-for-computer/csharp.png      |   3 -
     .../posts/laptop-for-computer/csharp.webp     |   3 +
     .../source/posts/laptop-for-computer/web.png  |   3 -
     .../source/posts/laptop-for-computer/web.webp |   3 +
     YaeBlog/source/posts/llvm-naive-0.md          |   9 +-
     .../llvm-naive-0/image-20240819213039409.png  |   3 -
     .../llvm-naive-0/image-20240819213039409.webp |   3 +
     .../llvm-naive-0/image-20240819213624927.png  |   3 -
     .../llvm-naive-0/image-20240819213624927.webp |   3 +
     .../llvm-naive-0/image-20240820221413791.png  |   3 -
     .../llvm-naive-0/image-20240820221413791.webp |   3 +
     .../llvm-naive-0/image-20240825171858276.png  |   3 -
     .../llvm-naive-0/image-20240825171858276.webp |   3 +
     YaeBlog/source/posts/minecraft-wayland.md     |  13 +-
     .../image-20240105212744116.png               |   3 -
     .../image-20240105212744116.webp              |   3 +
     .../image-20240105213439528.png               |   3 -
     .../image-20240105213439528.webp              |   3 +
     .../image-20240105213942445.png               |   3 -
     .../image-20240105213942445.webp              |   3 +
     YaeBlog/source/posts/mlir-standalone.md       |  15 +-
     .../image-20250319192618697.png               |   3 -
     .../image-20250319192618697.webp              |   3 +
     .../image-20250319194742171.png               |   3 -
     .../image-20250319194742171.webp              |   3 +
     .../image-20250319202218503.png               |   3 -
     .../image-20250319202218503.webp              |   3 +
     .../image-20250319202553644.png               |   3 -
     .../image-20250319202553644.webp              |   3 +
     .../image-20250319204057832.png               |   3 -
     .../image-20250319204057832.webp              |   3 +
     .../image-20250319204522857.png               |   3 -
     .../image-20250319204522857.webp              |   3 +
     .../image-20250319205520649.png               |   3 -
     .../image-20250319205520649.webp              |   3 +
     .../posts/parser-combinator-performance.md    |   9 +-
     .../image-20240819140523087.png               |   3 -
     .../image-20240819140523087.webp              |   3 +
     YaeBlog/source/posts/parser-combinator.md     |  15 +-
     .../image-20240813214315576.png               |   3 -
     .../image-20240813214315576.webp              |   3 +
     .../image-20240813220521028.png               |   3 -
     .../image-20240813220521028.webp              |   3 +
     .../image-20240813220530717.png               |   3 -
     .../image-20240813220530717.webp              |   3 +
     .../posts/program-design-introduction.md      |  11 +-
     .../posts/program-design-introduction/1.png   |   3 -
     .../posts/program-design-introduction/1.webp  |   3 +
     .../posts/program-design-introduction/2.png   |   3 -
     .../posts/program-design-introduction/2.webp  |   3 +
     .../posts/program-design-introduction/3.png   |   3 -
     .../posts/program-design-introduction/3.webp  |   3 +
     YaeBlog/source/posts/qt-learning.md           |  10 +-
     YaeBlog/source/posts/qt-learning/1.png        |   3 -
     YaeBlog/source/posts/qt-learning/1.webp       |   3 +
     .../posts/question-in-install-vs-2019.md      |   9 +-
     .../posts/question-in-install-vs-2019/1.png   |   3 -
     .../posts/question-in-install-vs-2019/1.webp  |   3 +
     .../source/posts/rust-drop-stack-overflow.md  |   7 +-
     .../image-20241105181144993.png               |   3 -
     .../image-20241105181144993.webp              |   3 +
     .../image-20241105181612954.png               |   3 -
     .../image-20241105181612954.webp              |   3 +
     .../image-20241105182036975.png               |   3 -
     .../image-20241105182036975.webp              |   3 +
     YaeBlog/source/posts/software-engineer.md     |  33 +--
     .../image-20240620211321957.png               |   3 -
     .../image-20240620211321957.webp              |   3 +
     .../image-20240620212101864.png               |   3 -
     .../image-20240620212101864.webp              |   3 +
     .../image-20240620214307906.png               |   3 -
     .../image-20240620214307906.webp              |   3 +
     .../image-20240620214739548.png               |   3 -
     .../image-20240620214739548.webp              |   3 +
     .../image-20240620215022645.png               |   3 -
     .../image-20240620215022645.webp              |   3 +
     .../image-20240620220155962.png               |   3 -
     .../image-20240620220155962.webp              |   3 +
     .../image-20240620224318982.png               |   3 -
     .../image-20240620224318982.webp              |   3 +
     .../image-20240620224519243.png               |   3 -
     .../image-20240620224519243.webp              |   3 +
     .../image-20240621114540033.png               |   3 -
     .../image-20240621114540033.webp              |   3 +
     .../image-20240623160826903.png               |   3 -
     .../image-20240623160826903.webp              |   3 +
     .../image-20240623162229404.png               |   3 -
     .../image-20240623162229404.webp              |   3 +
     .../image-20240623163647935.png               |   3 -
     .../image-20240623163647935.webp              |   3 +
     .../image-20240623170058679.png               |   3 -
     .../image-20240623170058679.webp              |   3 +
     .../posts/spring-boot-custom-authorize.md     |  11 +-
     .../image-20230727175807814.png               |   3 -
     .../image-20230727175807814.webp              |   3 +
     .../image-20230727175955817.png               |   3 -
     .../image-20230727175955817.webp              |   3 +
     YaeBlog/source/posts/vscode-in-browser.md     |   9 +-
     YaeBlog/source/posts/vscode-in-browser/1.png  |   3 -
     YaeBlog/source/posts/vscode-in-browser/1.webp |   3 +
     YaeBlog/source/posts/vscode-in-browser/2.png  |   3 -
     YaeBlog/source/posts/vscode-in-browser/2.webp |   3 +
     YaeBlog/source/posts/whats-new-of-dotnet-8.md |  15 +-
     .../AOTOptimizations4.png                     |   3 -
     .../AOTOptimizations4.webp                    |   3 +
     .../image-20231122100930849.png               |   3 -
     .../image-20231122100930849.webp              |   3 +
     .../image-20231122101012416.png               |   3 -
     .../image-20231122101012416.webp              |   3 +
     .../image-20231122101142080.png               |   3 -
     .../image-20231122101142080.webp              |   3 +
     YaeBlog/source/posts/wsl-setup-csapp.md       |  14 +-
     YaeBlog/source/posts/wsl-setup-csapp/1.png    |   3 -
     YaeBlog/source/posts/wsl-setup-csapp/1.webp   |   3 +
     YaeBlog/source/posts/wsl-setup-csapp/2.png    |   3 -
     YaeBlog/source/posts/wsl-setup-csapp/2.webp   |   3 +
     YaeBlog/source/posts/wsl-setup-csapp/3.png    |   3 -
     YaeBlog/source/posts/wsl-setup-csapp/3.webp   |   3 +
     287 files changed, 996 insertions(+), 710 deletions(-)
     create mode 100644 YaeBlog/Commands/Binders/ImageCompressServiceBinder.cs
     create mode 100644 YaeBlog/Exceptions/BlogCommandException.cs
     create mode 100644 YaeBlog/Models/BlogImageInfo.cs
     delete mode 100644 YaeBlog/Models/ImageScanResult.cs
     create mode 100644 YaeBlog/Services/ImageCompressService.cs
     delete mode 100644 YaeBlog/source/posts/2021-final/1.png
     create mode 100644 YaeBlog/source/posts/2021-final/1.webp
     delete mode 100644 YaeBlog/source/posts/2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.jpg
     create mode 100644 YaeBlog/source/posts/2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.webp
     delete mode 100644 YaeBlog/source/posts/2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.jpg
     create mode 100644 YaeBlog/source/posts/2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.webp
     delete mode 100644 YaeBlog/source/posts/2022-summer-vacation/result1.png
     create mode 100644 YaeBlog/source/posts/2022-summer-vacation/result1.webp
     delete mode 100644 YaeBlog/source/posts/2023-final/image-20240303165826486.png
     create mode 100644 YaeBlog/source/posts/2023-final/image-20240303165826486.webp
     delete mode 100644 YaeBlog/source/posts/2024-final/image-20250115171809775.png
     create mode 100644 YaeBlog/source/posts/2024-final/image-20250115171809775.webp
     delete mode 100644 YaeBlog/source/posts/big-homework/1.png
     create mode 100644 YaeBlog/source/posts/big-homework/1.webp
     delete mode 100644 YaeBlog/source/posts/build-blog-record/1.png
     create mode 100644 YaeBlog/source/posts/build-blog-record/1.webp
     delete mode 100644 YaeBlog/source/posts/build-blog-record/2.png
     create mode 100644 YaeBlog/source/posts/build-blog-record/2.webp
     delete mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824120646587.png
     create mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824120646587.webp
     delete mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824121425007.png
     create mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824121425007.webp
     delete mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824134158262.png
     create mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824134158262.webp
     delete mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824153514149.png
     create mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824153514149.webp
     delete mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824214145759.png
     create mode 100644 YaeBlog/source/posts/build-dotnet-from-source/image-20240824214145759.webp
     delete mode 100644 YaeBlog/source/posts/c-include-problems/1.png
     create mode 100644 YaeBlog/source/posts/c-include-problems/1.webp
     delete mode 100644 YaeBlog/source/posts/c-include-problems/2.png
     create mode 100644 YaeBlog/source/posts/c-include-problems/2.webp
     delete mode 100644 YaeBlog/source/posts/cncc-2024/image-20241102211959206.png
     create mode 100644 YaeBlog/source/posts/cncc-2024/image-20241102211959206.webp
     delete mode 100644 YaeBlog/source/posts/cncc-2024/image-20241102212355390.png
     create mode 100644 YaeBlog/source/posts/cncc-2024/image-20241102212355390.webp
     delete mode 100644 YaeBlog/source/posts/cncc-2024/image-20241102212536635.png
     create mode 100644 YaeBlog/source/posts/cncc-2024/image-20241102212536635.webp
     delete mode 100644 YaeBlog/source/posts/cncc-2024/image-20241102212738598.png
     create mode 100644 YaeBlog/source/posts/cncc-2024/image-20241102212738598.webp
     delete mode 100644 YaeBlog/source/posts/compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.png
     create mode 100644 YaeBlog/source/posts/compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.webp
     delete mode 100644 YaeBlog/source/posts/compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.png
     create mode 100644 YaeBlog/source/posts/compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.webp
     delete mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184855300.png
     create mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184855300.webp
     delete mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184949777.png
     create mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184949777.webp
     delete mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612190426368.png
     create mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612190426368.webp
     delete mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612192700169.png
     create mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612192700169.webp
     delete mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612193301372.png
     create mode 100644 YaeBlog/source/posts/computer-architecture-pipeline/image-20240612193301372.webp
     delete mode 100644 YaeBlog/source/posts/daily-linux-0/2023-01-12-13-28-38-Screenshot_20230112_132829.png
     create mode 100644 YaeBlog/source/posts/daily-linux-0/2023-01-12-13-28-38-Screenshot_20230112_132829.webp
     delete mode 100644 YaeBlog/source/posts/daily-linux-0/2023-01-12-13-36-45-Screenshot_20230112_133628.png
     create mode 100644 YaeBlog/source/posts/daily-linux-0/2023-01-12-13-36-45-Screenshot_20230112_133628.webp
     rename YaeBlog/source/posts/daily-linux-2/{df4211f6be2724b3b4725f7ce5a4078818844857.jpg => df4211f6be2724b3b4725f7ce5a4078818844857.avif} (100%)
     delete mode 100644 YaeBlog/source/posts/daily-linux-2/image-20230702205919301.png
     create mode 100644 YaeBlog/source/posts/daily-linux-2/image-20230702205919301.webp
     delete mode 100644 YaeBlog/source/posts/daily-linux-3/Screenshot_20230904_144149.png
     create mode 100644 YaeBlog/source/posts/daily-linux-3/Screenshot_20230904_144149.webp
     delete mode 100644 YaeBlog/source/posts/daily-linux-4/Screenshot_20240309_115143.png
     create mode 100644 YaeBlog/source/posts/daily-linux-4/Screenshot_20240309_115143.webp
     delete mode 100644 YaeBlog/source/posts/daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.jpg
     create mode 100644 YaeBlog/source/posts/daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.webp
     delete mode 100644 YaeBlog/source/posts/daily-linux-4/image-20240309130329784.png
     create mode 100644 YaeBlog/source/posts/daily-linux-4/image-20240309130329784.webp
     delete mode 100644 YaeBlog/source/posts/daily-linux-4/image-20240309131750535.png
     create mode 100644 YaeBlog/source/posts/daily-linux-4/image-20240309131750535.webp
     delete mode 100644 YaeBlog/source/posts/daily-linux-4/image-20240309134847166.png
     create mode 100644 YaeBlog/source/posts/daily-linux-4/image-20240309134847166.webp
     delete mode 100644 YaeBlog/source/posts/dotnet-performance-8/HeapsWhereNetObjectsLive.png
     create mode 100644 YaeBlog/source/posts/dotnet-performance-8/HeapsWhereNetObjectsLive.webp
     delete mode 100644 YaeBlog/source/posts/dotnet-performance-8/image-20240828135354598.png
     create mode 100644 YaeBlog/source/posts/dotnet-performance-8/image-20240828135354598.webp
     delete mode 100644 YaeBlog/source/posts/dotnet-performance-8/image-20240828155556375.png
     create mode 100644 YaeBlog/source/posts/dotnet-performance-8/image-20240828155556375.webp
     delete mode 100644 YaeBlog/source/posts/environment-setting/6.png
     create mode 100644 YaeBlog/source/posts/environment-setting/6.webp
     delete mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-06-36-image.png
     create mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-06-36-image.webp
     delete mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-20-46-image.png
     create mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-20-46-image.webp
     delete mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-21-11-image.png
     create mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-21-11-image.webp
     delete mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-24-26-image.png
     create mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-24-26-image.webp
     delete mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-20-image.png
     create mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-20-image.webp
     delete mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-42-image.png
     create mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-42-image.webp
     delete mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-16-00-10-image.png
     create mode 100644 YaeBlog/source/posts/genshin-gacha-1/2022-12-31-16-00-10-image.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214139.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214139.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214939.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214939.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241020142938110.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241020142938110.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241020155656219.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241020155656219.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241029123308139.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241029123308139.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241029163654675.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241029163654675.webp
     delete mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241103162259981.png
     create mode 100644 YaeBlog/source/posts/heterogeneous-programming-model/image-20241103162259981.webp
     delete mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.png
     create mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.webp
     delete mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.png
     create mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.webp
     delete mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.png
     create mode 100644 YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.webp
     delete mode 100644 YaeBlog/source/posts/install-pytorch/1.png
     create mode 100644 YaeBlog/source/posts/install-pytorch/1.webp
     delete mode 100644 YaeBlog/source/posts/install-pytorch/2.png
     create mode 100644 YaeBlog/source/posts/install-pytorch/2.webp
     delete mode 100644 YaeBlog/source/posts/install-pytorch/3.png
     create mode 100644 YaeBlog/source/posts/install-pytorch/3.webp
     delete mode 100644 YaeBlog/source/posts/install-pytorch/4.png
     create mode 100644 YaeBlog/source/posts/install-pytorch/4.webp
     delete mode 100644 YaeBlog/source/posts/laptop-for-computer/c.png
     create mode 100644 YaeBlog/source/posts/laptop-for-computer/c.webp
     delete mode 100644 YaeBlog/source/posts/laptop-for-computer/clion.png
     create mode 100644 YaeBlog/source/posts/laptop-for-computer/clion.webp
     delete mode 100644 YaeBlog/source/posts/laptop-for-computer/csharp.png
     create mode 100644 YaeBlog/source/posts/laptop-for-computer/csharp.webp
     delete mode 100644 YaeBlog/source/posts/laptop-for-computer/web.png
     create mode 100644 YaeBlog/source/posts/laptop-for-computer/web.webp
     delete mode 100644 YaeBlog/source/posts/llvm-naive-0/image-20240819213039409.png
     create mode 100644 YaeBlog/source/posts/llvm-naive-0/image-20240819213039409.webp
     delete mode 100644 YaeBlog/source/posts/llvm-naive-0/image-20240819213624927.png
     create mode 100644 YaeBlog/source/posts/llvm-naive-0/image-20240819213624927.webp
     delete mode 100644 YaeBlog/source/posts/llvm-naive-0/image-20240820221413791.png
     create mode 100644 YaeBlog/source/posts/llvm-naive-0/image-20240820221413791.webp
     delete mode 100644 YaeBlog/source/posts/llvm-naive-0/image-20240825171858276.png
     create mode 100644 YaeBlog/source/posts/llvm-naive-0/image-20240825171858276.webp
     delete mode 100644 YaeBlog/source/posts/minecraft-wayland/image-20240105212744116.png
     create mode 100644 YaeBlog/source/posts/minecraft-wayland/image-20240105212744116.webp
     delete mode 100644 YaeBlog/source/posts/minecraft-wayland/image-20240105213439528.png
     create mode 100644 YaeBlog/source/posts/minecraft-wayland/image-20240105213439528.webp
     delete mode 100644 YaeBlog/source/posts/minecraft-wayland/image-20240105213942445.png
     create mode 100644 YaeBlog/source/posts/minecraft-wayland/image-20240105213942445.webp
     delete mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319192618697.png
     create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319192618697.webp
     delete mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319194742171.png
     create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319194742171.webp
     delete mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319202218503.png
     create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319202218503.webp
     delete mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319202553644.png
     create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319202553644.webp
     delete mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319204057832.png
     create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319204057832.webp
     delete mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319204522857.png
     create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319204522857.webp
     delete mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319205520649.png
     create mode 100644 YaeBlog/source/posts/mlir-standalone/image-20250319205520649.webp
     delete mode 100644 YaeBlog/source/posts/parser-combinator-performance/image-20240819140523087.png
     create mode 100644 YaeBlog/source/posts/parser-combinator-performance/image-20240819140523087.webp
     delete mode 100644 YaeBlog/source/posts/parser-combinator/image-20240813214315576.png
     create mode 100644 YaeBlog/source/posts/parser-combinator/image-20240813214315576.webp
     delete mode 100644 YaeBlog/source/posts/parser-combinator/image-20240813220521028.png
     create mode 100644 YaeBlog/source/posts/parser-combinator/image-20240813220521028.webp
     delete mode 100644 YaeBlog/source/posts/parser-combinator/image-20240813220530717.png
     create mode 100644 YaeBlog/source/posts/parser-combinator/image-20240813220530717.webp
     delete mode 100644 YaeBlog/source/posts/program-design-introduction/1.png
     create mode 100644 YaeBlog/source/posts/program-design-introduction/1.webp
     delete mode 100644 YaeBlog/source/posts/program-design-introduction/2.png
     create mode 100644 YaeBlog/source/posts/program-design-introduction/2.webp
     delete mode 100644 YaeBlog/source/posts/program-design-introduction/3.png
     create mode 100644 YaeBlog/source/posts/program-design-introduction/3.webp
     delete mode 100644 YaeBlog/source/posts/qt-learning/1.png
     create mode 100644 YaeBlog/source/posts/qt-learning/1.webp
     delete mode 100644 YaeBlog/source/posts/question-in-install-vs-2019/1.png
     create mode 100644 YaeBlog/source/posts/question-in-install-vs-2019/1.webp
     delete mode 100644 YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181144993.png
     create mode 100644 YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181144993.webp
     delete mode 100644 YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181612954.png
     create mode 100644 YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181612954.webp
     delete mode 100644 YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105182036975.png
     create mode 100644 YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105182036975.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240620211321957.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240620211321957.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240620212101864.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240620212101864.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240620214307906.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240620214307906.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240620214739548.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240620214739548.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240620215022645.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240620215022645.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240620220155962.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240620220155962.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240620224318982.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240620224318982.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240620224519243.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240620224519243.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240621114540033.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240621114540033.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240623160826903.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240623160826903.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240623162229404.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240623162229404.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240623163647935.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240623163647935.webp
     delete mode 100644 YaeBlog/source/posts/software-engineer/image-20240623170058679.png
     create mode 100644 YaeBlog/source/posts/software-engineer/image-20240623170058679.webp
     delete mode 100644 YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175807814.png
     create mode 100644 YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175807814.webp
     delete mode 100644 YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175955817.png
     create mode 100644 YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175955817.webp
     delete mode 100644 YaeBlog/source/posts/vscode-in-browser/1.png
     create mode 100644 YaeBlog/source/posts/vscode-in-browser/1.webp
     delete mode 100644 YaeBlog/source/posts/vscode-in-browser/2.png
     create mode 100644 YaeBlog/source/posts/vscode-in-browser/2.webp
     delete mode 100644 YaeBlog/source/posts/whats-new-of-dotnet-8/AOTOptimizations4.png
     create mode 100644 YaeBlog/source/posts/whats-new-of-dotnet-8/AOTOptimizations4.webp
     delete mode 100644 YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122100930849.png
     create mode 100644 YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122100930849.webp
     delete mode 100644 YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101012416.png
     create mode 100644 YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101012416.webp
     delete mode 100644 YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101142080.png
     create mode 100644 YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101142080.webp
     delete mode 100644 YaeBlog/source/posts/wsl-setup-csapp/1.png
     create mode 100644 YaeBlog/source/posts/wsl-setup-csapp/1.webp
     delete mode 100644 YaeBlog/source/posts/wsl-setup-csapp/2.png
     create mode 100644 YaeBlog/source/posts/wsl-setup-csapp/2.webp
     delete mode 100644 YaeBlog/source/posts/wsl-setup-csapp/3.png
     create mode 100644 YaeBlog/source/posts/wsl-setup-csapp/3.webp
    
    diff --git a/.gitattributes b/.gitattributes
    index 1691f22..f5589bc 100644
    --- a/.gitattributes
    +++ b/.gitattributes
    @@ -1,2 +1,4 @@
     *.png filter=lfs diff=lfs merge=lfs -text
     *.jpg filter=lfs diff=lfs merge=lfs -text
    +*.avif filter=lfs diff=lfs merge=lfs -text
    +*.webp filter=lfs diff=lfs merge=lfs -text
    diff --git a/YaeBlog/Abstraction/IEssayScanService.cs b/YaeBlog/Abstraction/IEssayScanService.cs
    index f5aad84..d0d2f5e 100644
    --- a/YaeBlog/Abstraction/IEssayScanService.cs
    +++ b/YaeBlog/Abstraction/IEssayScanService.cs
    @@ -7,6 +7,4 @@ public interface IEssayScanService
         public Task ScanContents();
     
         public Task SaveBlogContent(BlogContent content, bool isDraft = true);
    -
    -    public Task ScanImages();
     }
    diff --git a/YaeBlog/Commands/Binders/ImageCompressServiceBinder.cs b/YaeBlog/Commands/Binders/ImageCompressServiceBinder.cs
    new file mode 100644
    index 0000000..95d9f51
    --- /dev/null
    +++ b/YaeBlog/Commands/Binders/ImageCompressServiceBinder.cs
    @@ -0,0 +1,21 @@
    +using System.CommandLine.Binding;
    +using YaeBlog.Abstraction;
    +using YaeBlog.Services;
    +
    +namespace YaeBlog.Commands.Binders;
    +
    +public sealed class ImageCompressServiceBinder : BinderBase
    +{
    +    protected override ImageCompressService GetBoundValue(BindingContext bindingContext)
    +    {
    +        bindingContext.AddService(provider =>
    +        {
    +            IEssayScanService essayScanService = provider.GetRequiredService();
    +            ILogger logger = provider.GetRequiredService>();
    +
    +            return new ImageCompressService(essayScanService, logger);
    +        });
    +
    +        return bindingContext.GetRequiredService();
    +    }
    +}
    diff --git a/YaeBlog/Commands/YaeBlogCommand.cs b/YaeBlog/Commands/YaeBlogCommand.cs
    index 94815c5..2f1d04e 100644
    --- a/YaeBlog/Commands/YaeBlogCommand.cs
    +++ b/YaeBlog/Commands/YaeBlogCommand.cs
    @@ -1,4 +1,6 @@
     using System.CommandLine;
    +using Microsoft.Extensions.Options;
    +using YaeBlog.Abstraction;
     using YaeBlog.Commands.Binders;
     using YaeBlog.Components;
     using YaeBlog.Extensions;
    @@ -19,6 +21,7 @@ public sealed class YaeBlogCommand
             AddNewCommand(_rootCommand);
             AddPublishCommand(_rootCommand);
             AddScanCommand(_rootCommand);
    +        AddCompressCommand(_rootCommand);
         }
     
         public Task RunAsync(string[] args)
    @@ -94,22 +97,20 @@ public sealed class YaeBlogCommand
             Argument filenameArgument = new(name: "blog name", description: "The created blog filename.");
             newCommand.AddArgument(filenameArgument);
     
    -        newCommand.SetHandler(async (file, _, _, essayScanService) =>
    +        newCommand.SetHandler(async (file, blogOption, _, essayScanService) =>
                 {
                     BlogContents contents = await essayScanService.ScanContents();
     
    -                if (contents.Posts.Any(content => content.FileName == file))
    +                if (contents.Posts.Any(content => content.BlogName == file))
                     {
                         Console.WriteLine("There exists the same title blog in posts.");
                         return;
                     }
     
    -                await essayScanService.SaveBlogContent(new BlogContent
    -                {
    -                    FileName = file,
    -                    FileContent = string.Empty,
    -                    Metadata = new MarkdownMetadata { Title = file, Date = DateTime.Now }
    -                });
    +                await essayScanService.SaveBlogContent(new BlogContent(
    +                    new FileInfo(Path.Combine(blogOption.Value.Root, "drafts", file + ".md")),
    +                    new MarkdownMetadata { Title = file, Date = DateTime.Now },
    +                    string.Empty, true, [], []));
     
                     Console.WriteLine($"Created new blog '{file}.");
                 }, filenameArgument, new BlogOptionsBinder(), new LoggerBinder(),
    @@ -126,15 +127,15 @@ public sealed class YaeBlogCommand
                 BlogContents contents = await essyScanService.ScanContents();
     
                 Console.WriteLine($"All {contents.Posts.Count} Posts:");
    -            foreach (BlogContent content in contents.Posts.OrderBy(x => x.FileName))
    +            foreach (BlogContent content in contents.Posts.OrderBy(x => x.BlogName))
                 {
    -                Console.WriteLine($" - {content.FileName}");
    +                Console.WriteLine($" - {content.BlogName}");
                 }
     
                 Console.WriteLine($"All {contents.Drafts.Count} Drafts:");
    -            foreach (BlogContent content in contents.Drafts.OrderBy(x => x.FileName))
    +            foreach (BlogContent content in contents.Drafts.OrderBy(x => x.BlogName))
                 {
    -                Console.WriteLine($" - {content.FileName}");
    +                Console.WriteLine($" - {content.BlogName}");
                 }
             }, new BlogOptionsBinder(), new LoggerBinder(), new EssayScanServiceBinder());
         }
    @@ -150,32 +151,39 @@ public sealed class YaeBlogCommand
     
             command.SetHandler(async (_, _, essayScanService, removeOptionValue) =>
             {
    -            ImageScanResult result = await essayScanService.ScanImages();
    +            BlogContents contents = await essayScanService.ScanContents();
    +            List unusedImages = (from content in contents
    +                from image in content.Images
    +                where image is { IsUsed: false }
    +                select image).ToList();
     
    -            if (result.UnusedImages.Count != 0)
    +            if (unusedImages.Count != 0)
                 {
                     Console.WriteLine("Found unused images:");
                     Console.WriteLine("HINT: use '--rm' to remove unused images.");
                 }
     
    -            foreach (FileInfo image in result.UnusedImages)
    +            foreach (BlogImageInfo image in unusedImages)
                 {
    -                Console.WriteLine($" - {image.FullName}");
    +                Console.WriteLine($" - {image.File.FullName}");
                 }
     
                 if (removeOptionValue)
                 {
    -                foreach (FileInfo image in result.UnusedImages)
    +                foreach (BlogImageInfo image in unusedImages)
                     {
    -                    image.Delete();
    +                    image.File.Delete();
                     }
                 }
     
                 Console.WriteLine("Used not existed images:");
     
    -            foreach (FileInfo image in result.NotFoundImages)
    +            foreach (BlogContent content in contents)
                 {
    -                Console.WriteLine($" - {image.FullName}");
    +                foreach (FileInfo file in content.NotfoundImages)
    +                {
    +                    Console.WriteLine($"- {file.Name} in {content.BlogName}");
    +                }
                 }
             }, new BlogOptionsBinder(), new LoggerBinder(), new EssayScanServiceBinder(), removeOption);
         }
    @@ -193,7 +201,7 @@ public sealed class YaeBlogCommand
                     BlogContents contents = await essayScanService.ScanContents();
     
                     BlogContent? content = (from blog in contents.Drafts
    -                    where blog.FileName == filename
    +                    where blog.BlogName == filename
                         select blog).FirstOrDefault();
     
                     if (content is null)
    @@ -202,14 +210,17 @@ public sealed class YaeBlogCommand
                         return;
                     }
     
    +                // 设置发布的时间
    +                content.Metadata.Date = DateTime.Now;
    +
                     // 将选中的博客文件复制到posts
                     await essayScanService.SaveBlogContent(content, isDraft: false);
     
                     // 复制图片文件夹
                     DirectoryInfo sourceImageDirectory =
    -                    new(Path.Combine(blogOptions.Value.Root, "drafts", content.FileName));
    +                    new(Path.Combine(blogOptions.Value.Root, "drafts", content.BlogName));
                     DirectoryInfo targetImageDirectory =
    -                    new(Path.Combine(blogOptions.Value.Root, "posts", content.FileName));
    +                    new(Path.Combine(blogOptions.Value.Root, "posts", content.BlogName));
     
                     if (sourceImageDirectory.Exists)
                     {
    @@ -223,9 +234,30 @@ public sealed class YaeBlogCommand
                     }
     
                     // 删除原始的文件
    -                FileInfo sourceBlogFile = new(Path.Combine(blogOptions.Value.Root, "drafts", content.FileName + ".md"));
    +                FileInfo sourceBlogFile = new(Path.Combine(blogOptions.Value.Root, "drafts", content.BlogName + ".md"));
                     sourceBlogFile.Delete();
                 }, new BlogOptionsBinder(),
                 new LoggerBinder(), new EssayScanServiceBinder(), filenameArgument);
         }
    +
    +    private static void AddCompressCommand(RootCommand rootCommand)
    +    {
    +        Command command = new("compress", "Compress png/jpeg image to webp image to reduce size.");
    +        rootCommand.Add(command);
    +
    +        Option dryRunOption = new("--dry-run", description: "Dry run the compression task but not write.",
    +            getDefaultValue: () => false);
    +        command.AddOption(dryRunOption);
    +
    +        command.SetHandler(ImageCommandHandler,
    +            new BlogOptionsBinder(), new LoggerBinder(), new LoggerBinder(),
    +            new EssayScanServiceBinder(), new ImageCompressServiceBinder(), dryRunOption);
    +    }
    +
    +    private static async Task ImageCommandHandler(IOptions _, ILogger _1,
    +        ILogger _2,
    +        IEssayScanService _3, ImageCompressService imageCompressService, bool dryRun)
    +    {
    +        await imageCompressService.Compress(dryRun);
    +    }
     }
    diff --git a/YaeBlog/Exceptions/BlogCommandException.cs b/YaeBlog/Exceptions/BlogCommandException.cs
    new file mode 100644
    index 0000000..0db929d
    --- /dev/null
    +++ b/YaeBlog/Exceptions/BlogCommandException.cs
    @@ -0,0 +1,12 @@
    +namespace YaeBlog.Core.Exceptions;
    +
    +public class BlogCommandException : Exception
    +{
    +    public BlogCommandException(string message) : base(message)
    +    {
    +    }
    +
    +    public BlogCommandException(string message, Exception innerException) : base(message, innerException)
    +    {
    +    }
    +}
    diff --git a/YaeBlog/Models/BlogContent.cs b/YaeBlog/Models/BlogContent.cs
    index 6ab5b7d..1765249 100644
    --- a/YaeBlog/Models/BlogContent.cs
    +++ b/YaeBlog/Models/BlogContent.cs
    @@ -1,12 +1,20 @@
     namespace YaeBlog.Models;
     
    -public class BlogContent
    +/// 
    +/// 单个博客文件的所有数据和元数据
    +/// 
    +/// 博客文件
    +/// 文件中的MD元数据
    +/// 文件内容
    +/// 是否为草稿
    +/// 博客中使用的文件
    +public record BlogContent(
    +    FileInfo BlogFile,
    +    MarkdownMetadata Metadata,
    +    string Content,
    +    bool IsDraft,
    +    List Images,
    +    List NotfoundImages)
     {
    -    public required string FileName { get; init; }
    -
    -    public required MarkdownMetadata Metadata { get; init; }
    -
    -    public required string FileContent { get; set; }
    -
    -    public bool IsDraft { get; set; } = false;
    +    public string BlogName => BlogFile.Name.Split('.')[0];
     }
    diff --git a/YaeBlog/Models/BlogContents.cs b/YaeBlog/Models/BlogContents.cs
    index 8a6f7d9..11dd937 100644
    --- a/YaeBlog/Models/BlogContents.cs
    +++ b/YaeBlog/Models/BlogContents.cs
    @@ -1,10 +1,15 @@
    -using System.Collections.Concurrent;
    +using System.Collections;
    +using System.Collections.Concurrent;
     
     namespace YaeBlog.Models;
     
    -public sealed class BlogContents(ConcurrentBag drafts, ConcurrentBag posts)
    +public record BlogContents(ConcurrentBag Drafts, ConcurrentBag Posts)
    +    : IEnumerable
     {
    -    public ConcurrentBag Drafts { get; } = drafts;
    +    IEnumerator IEnumerable.GetEnumerator()
    +    {
    +        return Posts.Concat(Drafts).GetEnumerator();
    +    }
     
    -    public ConcurrentBag Posts { get; } = posts;
    +    public IEnumerator GetEnumerator() => ((IEnumerable)this).GetEnumerator();
     }
    diff --git a/YaeBlog/Models/BlogImageInfo.cs b/YaeBlog/Models/BlogImageInfo.cs
    new file mode 100644
    index 0000000..ee63641
    --- /dev/null
    +++ b/YaeBlog/Models/BlogImageInfo.cs
    @@ -0,0 +1,44 @@
    +using System.Text;
    +
    +namespace YaeBlog.Models;
    +
    +public record BlogImageInfo(FileInfo File, long Width, long Height, string MineType, byte[] Content, bool IsUsed)
    +    : IComparable
    +{
    +    public int Size => Content.Length;
    +
    +    public override string ToString()
    +    {
    +        StringBuilder builder = new();
    +
    +        builder.AppendLine($"Blog image {File.Name}:");
    +        builder.AppendLine($"\tWidth: {Width}; Height: {Height}");
    +        builder.AppendLine($"\tSize: {FormatSize()}");
    +        builder.AppendLine($"\tImage Format: {MineType}");
    +
    +        return builder.ToString();
    +    }
    +
    +    public int CompareTo(BlogImageInfo? other)
    +    {
    +        if (other is null)
    +        {
    +            return -1;
    +        }
    +
    +        return other.Size.CompareTo(Size);
    +    }
    +
    +    private string FormatSize()
    +    {
    +        double size = Size;
    +        if (size / 1024 > 3)
    +        {
    +            size /= 1024;
    +
    +            return size / 1024 > 3 ? $"{size / 1024}MB" : $"{size}KB";
    +        }
    +
    +        return $"{size}B";
    +    }
    +}
    diff --git a/YaeBlog/Models/ImageScanResult.cs b/YaeBlog/Models/ImageScanResult.cs
    deleted file mode 100644
    index 85f3cc7..0000000
    --- a/YaeBlog/Models/ImageScanResult.cs
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -namespace YaeBlog.Models;
    -
    -public record struct ImageScanResult(List UnusedImages, List NotFoundImages);
    diff --git a/YaeBlog/Services/EssayScanService.cs b/YaeBlog/Services/EssayScanService.cs
    index 1ef9b13..be50ab6 100644
    --- a/YaeBlog/Services/EssayScanService.cs
    +++ b/YaeBlog/Services/EssayScanService.cs
    @@ -1,5 +1,7 @@
     using System.Collections.Concurrent;
     using System.Text.RegularExpressions;
    +using Imageflow.Bindings;
    +using Imageflow.Fluent;
     using Microsoft.Extensions.Options;
     using YaeBlog.Abstraction;
     using YaeBlog.Core.Exceptions;
    @@ -9,17 +11,30 @@ using YamlDotNet.Serialization;
     
     namespace YaeBlog.Services;
     
    -public partial class EssayScanService(
    -    ISerializer yamlSerializer,
    -    IDeserializer yamlDeserializer,
    -    IOptions blogOptions,
    -    ILogger logger) : IEssayScanService
    +public partial class EssayScanService : IEssayScanService
     {
    -    private readonly BlogOptions _blogOptions = blogOptions.Value;
    +    private readonly BlogOptions _blogOptions;
    +    private readonly ISerializer _yamlSerializer;
    +    private readonly IDeserializer _yamlDeserializer;
    +    private readonly ILogger _logger;
    +
    +    public EssayScanService(ISerializer yamlSerializer,
    +        IDeserializer yamlDeserializer,
    +        IOptions blogOptions,
    +        ILogger logger)
    +    {
    +        _yamlSerializer = yamlSerializer;
    +        _yamlDeserializer = yamlDeserializer;
    +        _logger = logger;
    +        _blogOptions = blogOptions.Value;
    +        RootDirectory = ValidateRootDirectory();
    +    }
    +
    +    private DirectoryInfo RootDirectory { get; }
     
         public async Task ScanContents()
         {
    -        ValidateDirectory(_blogOptions.Root, out DirectoryInfo drafts, out DirectoryInfo posts);
    +        ValidateDirectory(out DirectoryInfo drafts, out DirectoryInfo posts);
     
             return new BlogContents(
                 await ScanContentsInternal(drafts, true),
    @@ -28,82 +43,92 @@ public partial class EssayScanService(
     
         public async Task SaveBlogContent(BlogContent content, bool isDraft = true)
         {
    -        ValidateDirectory(_blogOptions.Root, out DirectoryInfo drafts, out DirectoryInfo posts);
    +        ValidateDirectory(out DirectoryInfo drafts, out DirectoryInfo posts);
     
             FileInfo targetFile = isDraft
    -            ? new FileInfo(Path.Combine(drafts.FullName, content.FileName + ".md"))
    -            : new FileInfo(Path.Combine(posts.FullName, content.FileName + ".md"));
    -
    -        if (!isDraft)
    -        {
    -            content.Metadata.Date = DateTime.Now;
    -        }
    +            ? new FileInfo(Path.Combine(drafts.FullName, content.BlogName + ".md"))
    +            : new FileInfo(Path.Combine(posts.FullName, content.BlogName + ".md"));
     
             if (targetFile.Exists)
             {
    -            logger.LogWarning("Blog {} exists, overriding.", targetFile.Name);
    +            _logger.LogWarning("Blog {} exists, overriding.", targetFile.Name);
             }
     
             await using StreamWriter writer = targetFile.CreateText();
     
             await writer.WriteAsync("---\n");
    -        await writer.WriteAsync(yamlSerializer.Serialize(content.Metadata));
    +        await writer.WriteAsync(_yamlSerializer.Serialize(content.Metadata));
             await writer.WriteAsync("---\n");
     
    -        if (isDraft)
    +        if (string.IsNullOrEmpty(content.Content) && isDraft)
             {
    +            // 如果博客为操作且内容为空
    +            // 创建简介隔断符号
                 await writer.WriteLineAsync("");
             }
             else
             {
    -            await writer.WriteAsync(content.FileContent);
    +            await writer.WriteAsync(content.Content);
             }
    +
    +        // 保存图片文件
    +        await Task.WhenAll(from image in content.Images
    +            select File.WriteAllBytesAsync(image.File.FullName, image.Content));
         }
     
    +    private record struct BlogResult(
    +        FileInfo BlogFile,
    +        string BlogContent,
    +        List Images,
    +        List NotFoundImages);
    +
         private async Task> ScanContentsInternal(DirectoryInfo directory, bool isDraft)
         {
    -        // 扫描以md结果的但是不是隐藏文件的文件
    +        // 扫描以md结尾且不是隐藏文件的文件
             IEnumerable markdownFiles = from file in directory.EnumerateFiles()
                 where file.Extension == ".md" && !file.Name.StartsWith('.')
                 select file;
     
    -        ConcurrentBag<(string, string)> fileContents = [];
    +        ConcurrentBag fileContents = [];
     
             await Parallel.ForEachAsync(markdownFiles, async (file, token) =>
             {
                 using StreamReader reader = file.OpenText();
    -            fileContents.Add((file.Name, await reader.ReadToEndAsync(token)));
    +            string blogName = file.Name.Split('.')[0];
    +            string blogContent = await reader.ReadToEndAsync(token);
    +            ImageResult imageResult =
    +                await ScanImagePreBlog(directory, blogName,
    +                    blogContent);
    +
    +            fileContents.Add(new BlogResult(file, blogContent, imageResult.Images, imageResult.NotfoundImages));
             });
     
             ConcurrentBag contents = [];
     
             await Task.Run(() =>
             {
    -            foreach ((string filename, string content) in fileContents)
    +            foreach (BlogResult blog in fileContents)
                 {
    -                int endPos = content.IndexOf("---", 4, StringComparison.Ordinal);
    -                if (!content.StartsWith("---") || endPos is -1 or 0)
    +                int endPos = blog.BlogContent.IndexOf("---", 4, StringComparison.Ordinal);
    +                if (!blog.BlogContent.StartsWith("---") || endPos is -1 or 0)
                     {
    -                    logger.LogWarning("Failed to parse metadata from {}, skipped.", filename);
    +                    _logger.LogWarning("Failed to parse metadata from {}, skipped.", blog.BlogFile.Name);
                         return;
                     }
     
    -                string metadataString = content[4..endPos];
    +                string metadataString = blog.BlogContent[4..endPos];
     
                     try
                     {
    -                    MarkdownMetadata metadata = yamlDeserializer.Deserialize(metadataString);
    -                    logger.LogDebug("Scan metadata title: '{}' for {}.", metadata.Title, filename);
    +                    MarkdownMetadata metadata = _yamlDeserializer.Deserialize(metadataString);
    +                    _logger.LogDebug("Scan metadata title: '{}' for {}.", metadata.Title, blog.BlogFile.Name);
     
    -                    contents.Add(new BlogContent
    -                    {
    -                        FileName = filename[..^3], Metadata = metadata, FileContent = content[(endPos + 3)..],
    -                        IsDraft = isDraft
    -                    });
    +                    contents.Add(new BlogContent(blog.BlogFile, metadata, blog.BlogContent[(endPos + 3)..], isDraft,
    +                        blog.Images, blog.NotFoundImages));
                     }
                     catch (YamlException e)
                     {
    -                    logger.LogWarning("Failed to parser metadata from {} due to {}, skipping", filename, e);
    +                    _logger.LogWarning("Failed to parser metadata from {} due to {}, skipping", blog.BlogFile.Name, e);
                     }
                 }
             });
    @@ -111,99 +136,96 @@ public partial class EssayScanService(
             return contents;
         }
     
    -    public async Task ScanImages()
    +    private record struct ImageResult(List Images, List NotfoundImages);
    +
    +    private async Task ScanImagePreBlog(DirectoryInfo directory, string blogName, string content)
         {
    -        BlogContents contents = await ScanContents();
    -        ValidateDirectory(_blogOptions.Root, out DirectoryInfo drafts, out DirectoryInfo posts);
    +        MatchCollection matchResult = ImagePattern.Matches(content);
    +        DirectoryInfo imageDirectory = new(Path.Combine(directory.FullName, blogName));
     
    -        List unusedFiles = [];
    -        List notFoundFiles = [];
    +        Dictionary usedImages = imageDirectory.Exists
    +            ? imageDirectory.EnumerateFiles().ToDictionary(file => file.FullName, _ => false)
    +            : [];
    +        List notFoundImages = [];
     
    -        ImageScanResult draftResult = await ScanUnusedImagesInternal(contents.Drafts, drafts);
    -        ImageScanResult postResult = await ScanUnusedImagesInternal(contents.Posts, posts);
    -
    -        unusedFiles.AddRange(draftResult.UnusedImages);
    -        notFoundFiles.AddRange(draftResult.NotFoundImages);
    -        unusedFiles.AddRange(postResult.UnusedImages);
    -        notFoundFiles.AddRange(postResult.NotFoundImages);
    -
    -        return new ImageScanResult(unusedFiles, notFoundFiles);
    -    }
    -
    -    private static Task ScanUnusedImagesInternal(IEnumerable contents,
    -        DirectoryInfo root)
    -    {
    -        ConcurrentBag unusedImage = [];
    -        ConcurrentBag notFoundImage = [];
    -
    -        Parallel.ForEach(contents, content =>
    +        foreach (Match match in matchResult)
             {
    -            MatchCollection result = ImagePattern.Matches(content.FileContent);
    -            DirectoryInfo imageDirectory = new(Path.Combine(root.FullName, content.FileName));
    +            string imageName = match.Groups[1].Value;
     
    -            Dictionary usedDictionary;
    +            // 判断md文件中的图片名称中是否包含文件夹名称
    +            // 例如 blog-1/image.png 或者 image.png
    +            // 如果不带文件夹名称
    +            // 默认添加同博客名文件夹
    +            FileInfo usedFile = imageName.Contains(blogName)
    +                ? new FileInfo(Path.Combine(directory.FullName, imageName))
    +                : new FileInfo(Path.Combine(directory.FullName, blogName, imageName));
     
    -            if (imageDirectory.Exists)
    +            if (usedImages.TryGetValue(usedFile.FullName, out _))
                 {
    -                usedDictionary = (from file in imageDirectory.EnumerateFiles()
    -                    select new KeyValuePair(file.FullName, false)).ToDictionary();
    +                usedImages[usedFile.FullName] = true;
                 }
                 else
                 {
    -                usedDictionary = [];
    +                notFoundImages.Add(usedFile);
                 }
    +        }
     
    -            foreach (Match match in result)
    -            {
    -                string imageName = match.Groups[1].Value;
    +        List images = (await Task.WhenAll((from pair in usedImages
    +            select GetImageInfo(new FileInfo(pair.Key), pair.Value)).ToArray())).ToList();
     
    -                FileInfo usedFile = imageName.Contains(content.FileName)
    -                    ? new FileInfo(Path.Combine(root.FullName, imageName))
    -                    : new FileInfo(Path.Combine(root.FullName, content.FileName, imageName));
    +        return new ImageResult(images, notFoundImages);
    +    }
     
    -                if (usedDictionary.TryGetValue(usedFile.FullName, out _))
    -                {
    -                    usedDictionary[usedFile.FullName] = true;
    -                }
    -                else
    -                {
    -                    notFoundImage.Add(usedFile);
    -                }
    -            }
    +    private static async Task GetImageInfo(FileInfo file, bool isUsed)
    +    {
    +        byte[] image = await File.ReadAllBytesAsync(file.FullName);
     
    -            foreach (KeyValuePair pair in usedDictionary.Where(p => !p.Value))
    -            {
    -                unusedImage.Add(new FileInfo(pair.Key));
    -            }
    -        });
    +        if (file.Extension is ".jpg" or ".jpeg" or ".png")
    +        {
    +            ImageInfo imageInfo =
    +                await ImageJob.GetImageInfoAsync(MemorySource.Borrow(image), SourceLifetime.NowOwnedAndDisposedByTask);
     
    -        return Task.FromResult(new ImageScanResult(unusedImage.ToList(), notFoundImage.ToList()));
    +            return new BlogImageInfo(file, imageInfo.ImageWidth, imageInfo.ImageWidth, imageInfo.PreferredMimeType,
    +                image, isUsed);
    +        }
    +
    +        return new BlogImageInfo(file, 0, 0, file.Extension switch
    +        {
    +            "svg" => "image/svg",
    +            "avif" => "image/avif",
    +            _ => string.Empty
    +        }, image, isUsed);
         }
     
         [GeneratedRegex(@"\!\[.*?\]\((.*?)\)")]
         private static partial Regex ImagePattern { get; }
     
    -    private void ValidateDirectory(string root, out DirectoryInfo drafts, out DirectoryInfo posts)
    +
    +    private DirectoryInfo ValidateRootDirectory()
         {
    -        root = Path.Combine(Environment.CurrentDirectory, root);
    -        DirectoryInfo rootDirectory = new(root);
    +        DirectoryInfo rootDirectory = new(Path.Combine(Environment.CurrentDirectory, _blogOptions.Root));
     
             if (!rootDirectory.Exists)
             {
    -            throw new BlogFileException($"'{root}' is not a directory.");
    +            throw new BlogFileException($"'{_blogOptions.Root}' is not a directory.");
             }
     
    -        if (rootDirectory.EnumerateDirectories().All(dir => dir.Name != "drafts"))
    +        return rootDirectory;
    +    }
    +
    +    private void ValidateDirectory(out DirectoryInfo drafts, out DirectoryInfo posts)
    +    {
    +        if (RootDirectory.EnumerateDirectories().All(dir => dir.Name != "drafts"))
             {
    -            throw new BlogFileException($"'{root}/drafts' not exists.");
    +            throw new BlogFileException($"'{_blogOptions.Root}/drafts' not exists.");
             }
     
    -        if (rootDirectory.EnumerateDirectories().All(dir => dir.Name != "posts"))
    +        if (RootDirectory.EnumerateDirectories().All(dir => dir.Name != "posts"))
             {
    -            throw new BlogFileException($"'{root}/posts' not exists.");
    +            throw new BlogFileException($"'{_blogOptions.Root}/posts' not exists.");
             }
     
    -        drafts = new DirectoryInfo(Path.Combine(root, "drafts"));
    -        posts = new DirectoryInfo(Path.Combine(root, "posts"));
    +        drafts = new DirectoryInfo(Path.Combine(_blogOptions.Root, "drafts"));
    +        posts = new DirectoryInfo(Path.Combine(_blogOptions.Root, "posts"));
         }
     }
    diff --git a/YaeBlog/Services/ImageCompressService.cs b/YaeBlog/Services/ImageCompressService.cs
    new file mode 100644
    index 0000000..831e230
    --- /dev/null
    +++ b/YaeBlog/Services/ImageCompressService.cs
    @@ -0,0 +1,119 @@
    +using Imageflow.Fluent;
    +using YaeBlog.Abstraction;
    +using YaeBlog.Core.Exceptions;
    +using YaeBlog.Models;
    +
    +namespace YaeBlog.Services;
    +
    +public sealed class ImageCompressService(IEssayScanService essayScanService, ILogger logger)
    +{
    +    private record struct CompressResult(BlogImageInfo ImageInfo, byte[] CompressContent);
    +
    +    public async Task> ScanUsedImages()
    +    {
    +        BlogContents contents = await essayScanService.ScanContents();
    +        List originalImages = (from content in contents.Posts.Concat(contents.Drafts)
    +            from image in content.Images
    +            where image.IsUsed
    +            select image).ToList();
    +
    +        originalImages.Sort();
    +
    +        return originalImages;
    +    }
    +
    +    public async Task Compress(bool dryRun)
    +    {
    +        BlogContents contents = await essayScanService.ScanContents();
    +
    +        // 筛选需要压缩的图片
    +        // 即图片被博客使用且是jpeg/png格式
    +        List needCompressContents = (from content in contents
    +            where content.Images.Any(i => i is { IsUsed: true } and { File.Extension: ".jpg" or ".jpeg" or ".png" })
    +            select content).ToList();
    +
    +        if (needCompressContents.Count == 0)
    +        {
    +            return;
    +        }
    +
    +        int uncompressedSize = 0;
    +        int compressedSize = 0;
    +        List compressedContent = new(needCompressContents.Count);
    +
    +        foreach (BlogContent content in needCompressContents)
    +        {
    +            List uncompressedImages = (from image in content.Images
    +                where image is { IsUsed: true } and { File.Extension: ".jpg" or ".jpeg" or ".png" }
    +                select image).ToList();
    +
    +            uncompressedSize += uncompressedImages.Select(i => i.Size).Sum();
    +
    +            foreach (BlogImageInfo image in uncompressedImages)
    +            {
    +                logger.LogInformation("Uncompressed image: {} belonging to blog {}.", image.File.Name,
    +                    content.BlogName);
    +            }
    +
    +            CompressResult[] compressedImages = (await Task.WhenAll(from image in uncompressedImages
    +                select Task.Run(async () => new CompressResult(image, await ConvertToWebp(image))))).ToArray();
    +
    +            compressedSize += compressedImages.Select(i => i.CompressContent.Length).Sum();
    +
    +            // 直接在原有的图片列表上添加图片
    +            List images = content.Images.Concat(from r in compressedImages
    +                select r.ImageInfo with
    +                {
    +                    File = new FileInfo(r.ImageInfo.File.FullName.Split('.')[0] + ".webp"),
    +                    Content = r.CompressContent,
    +                    MineType = "image/webp"
    +                }).ToList();
    +            // 修改文本
    +            string blogContent = compressedImages.Aggregate(content.Content, (c, r) =>
    +            {
    +                string originalName = r.ImageInfo.File.Name;
    +                string outputName = originalName.Split('.')[0] + ".webp";
    +
    +                return c.Replace(originalName, outputName);
    +            });
    +
    +            compressedContent.Add(content with { Images = images, Content = blogContent });
    +        }
    +
    +        logger.LogInformation("Compression ratio: {}%.", (double)compressedSize / uncompressedSize * 100.0);
    +
    +        if (dryRun is false)
    +        {
    +            await Task.WhenAll(from content in compressedContent
    +                select essayScanService.SaveBlogContent(content, content.IsDraft));
    +        }
    +    }
    +
    +    private static async Task ConvertToWebp(BlogImageInfo image)
    +    {
    +        using ImageJob job = new();
    +        BuildJobResult result = await job.Decode(MemorySource.Borrow(image.Content))
    +            .Branch(f => f.EncodeToBytes(new WebPLosslessEncoder()))
    +            .EncodeToBytes(new WebPLossyEncoder(75))
    +            .Finish()
    +            .InProcessAsync();
    +
    +        // 超过128KB的图片使用有损压缩
    +        // 反之使用无损压缩
    +
    +        ArraySegment? losslessImage = result.TryGet(1)?.TryGetBytes();
    +        ArraySegment? lossyImage = result.TryGet(2)?.TryGetBytes();
    +
    +        if (image.Size <= 128 * 1024 && losslessImage.HasValue)
    +        {
    +            return losslessImage.Value.ToArray();
    +        }
    +
    +        if (lossyImage.HasValue)
    +        {
    +            return lossyImage.Value.ToArray();
    +        }
    +
    +        throw new BlogCommandException($"Failed to convert {image.File.Name} to webp format: return value is null.");
    +    }
    +}
    diff --git a/YaeBlog/Services/RendererService.cs b/YaeBlog/Services/RendererService.cs
    index dda44ee..6105694 100644
    --- a/YaeBlog/Services/RendererService.cs
    +++ b/YaeBlog/Services/RendererService.cs
    @@ -41,14 +41,14 @@ public partial class RendererService(
                 uint wordCount = GetWordCount(content);
                 BlogEssay essay = new()
                 {
    -                Title = content.Metadata.Title ?? content.FileName,
    -                FileName = content.FileName,
    +                Title = content.Metadata.Title ?? content.BlogName,
    +                FileName = content.BlogName,
                     IsDraft = content.IsDraft,
                     Description = GetDescription(content),
                     WordCount = wordCount,
                     ReadTime = CalculateReadTime(wordCount),
                     PublishTime = content.Metadata.Date ?? DateTime.Now,
    -                HtmlContent = content.FileContent
    +                HtmlContent = content.Content
                 };
     
                 if (content.Metadata.Tags is not null)
    @@ -156,17 +156,17 @@ public partial class RendererService(
         private string GetDescription(BlogContent content)
         {
             const string delimiter = "";
    -        int pos = content.FileContent.IndexOf(delimiter, StringComparison.Ordinal);
    +        int pos = content.Content.IndexOf(delimiter, StringComparison.Ordinal);
             bool breakSentence = false;
     
             if (pos == -1)
             {
                 // 自动截取前50个字符
    -            pos = content.FileContent.Length < 50 ? content.FileContent.Length : 50;
    +            pos = content.Content.Length < 50 ? content.Content.Length : 50;
                 breakSentence = true;
             }
     
    -        string rawContent = content.FileContent[..pos];
    +        string rawContent = content.Content[..pos];
             MatchCollection matches = DescriptionPattern.Matches(rawContent);
     
             StringBuilder builder = new();
    @@ -182,18 +182,18 @@ public partial class RendererService(
     
             string description = builder.ToString();
     
    -        logger.LogDebug("Description of {} is {}.", content.FileName,
    +        logger.LogDebug("Description of {} is {}.", content.BlogName,
                 description);
             return description;
         }
     
         private uint GetWordCount(BlogContent content)
         {
    -        int count = (from c in content.FileContent
    +        int count = (from c in content.Content
                 where char.IsLetterOrDigit(c)
                 select c).Count();
     
    -        logger.LogDebug("Word count of {} is {}", content.FileName,
    +        logger.LogDebug("Word count of {} is {}", content.BlogName,
                 count);
             return (uint)count;
         }
    diff --git a/YaeBlog/YaeBlog.csproj b/YaeBlog/YaeBlog.csproj
    index 546dc43..133503a 100644
    --- a/YaeBlog/YaeBlog.csproj
    +++ b/YaeBlog/YaeBlog.csproj
    @@ -1,6 +1,8 @@
     
     
       
    +    
    +    
         
         
         
    diff --git a/YaeBlog/source/posts/2021-final.md b/YaeBlog/source/posts/2021-final.md
    index 619d73a..7f54bbd 100644
    --- a/YaeBlog/source/posts/2021-final.md
    +++ b/YaeBlog/source/posts/2021-final.md
    @@ -1,11 +1,12 @@
     ---
     title: 2021年终总结
    -date: 2022-01-12 16:27:19
    +date: 2022-01-12T16:27:19.0000000
     tags:
    -    - 杂谈
    -    - 年终总结
    +- 杂谈
    +- 年终总结
     ---
     
    +
     2021年已经过去,2022年已经来临。每每一年开始的时候,我都会展开一张纸或者新建一个文档,思量着又是一年时光,也该同诸大杂志一般,写几句意味深长的话语,怀念过去的时光,也祝福未来的自己。可往往脑海中已是三万字的长篇,落在笔头却又是一个字都没有了。  
     如今跨年的时候已经过去,朋友圈中已经不见文案的踪影,我也该重新提笔,细说自己2021年中做过的种种。  
     
    @@ -23,7 +24,7 @@ tags:
     在前12年的学生生涯中,我们都在期待着这一次的暑假,以为在这个没有作业的假期里,我们就可以充分的享受人间的美好。可是,当时我们不知道,这人间的烦恼,可不止作业这一种,无论是突如其来的疫情导致开学延期,还是等待录取时的不安。  
     虽说在暑假时,拥有了自己的笔记本电脑,可是在高中三年屯下的游戏还是没有玩几个,看来我也是“喜加一”的受害者。虽然在高考后入坑了原神,但是假期间我并没有太过投入的玩。  
     暑假下定决心要好好的学一学,可是看着我gitee上暑假期间那稀疏的提交,我就知道我又摸了一个暑假的鱼。  
    -![gitee贡献](./2021-final/1.png)
    +![gitee贡献](./2021-final/1.webp)
     即使我想写的很多项目都没有被扎实的推进下来,但是学习的一些的C语言还是让我受益匪浅。  
     现在看来,这个假期真是,**学也没有学好,耍也没有耍好**的典型。   
     
    diff --git a/YaeBlog/source/posts/2021-final/1.png b/YaeBlog/source/posts/2021-final/1.png
    deleted file mode 100644
    index 81022e2..0000000
    --- a/YaeBlog/source/posts/2021-final/1.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:6c35d649045aeb7b80a4d1b18290feabb367a3841d87b727d550dd06566801b6
    -size 8340
    diff --git a/YaeBlog/source/posts/2021-final/1.webp b/YaeBlog/source/posts/2021-final/1.webp
    new file mode 100644
    index 0000000..0f96b20
    --- /dev/null
    +++ b/YaeBlog/source/posts/2021-final/1.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:435af818a4675aad44016e5c390c101ede156d1dfa445e851de32bdfb342d915
    +size 2982
    diff --git a/YaeBlog/source/posts/2022-final.md b/YaeBlog/source/posts/2022-final.md
    index 91637e6..fa58ecc 100644
    --- a/YaeBlog/source/posts/2022-final.md
    +++ b/YaeBlog/source/posts/2022-final.md
    @@ -1,12 +1,13 @@
     ---
     title: 2022年终总结
    +date: 2022-12-30T14:58:12.0000000
     tags:
    -  - 杂谈
    -  - 年终总结
    -date: 2022-12-30 14:58:12
    +- 杂谈
    +- 年终总结
     ---
     
     
    +
     2022是困难的一年。我们需要为2023年做好准备。
     
     
    @@ -57,11 +58,11 @@ date: 2022-12-30 14:58:12
     
     小小的总结一下:2022年可以算得上是一事无成的一年,还搞砸了不少的事情。在写代码上进展有限,成绩上大幅倒退,说好的六级英语和大学物理竞赛都没有参加,在年末应对疫情进展的时候更是把“不知所措”这个成语诠释的淋漓尽致。
     
    -![](./2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.jpg)
    +![](./2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.webp)
     
     关于今年的人际交往和社会关系,我愿意用QQ2022年年终总结中的一张截屏来总结,这张图片透漏出一种无可救药的悲伤。
     
    -![](./2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.jpg)
    +![](./2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.webp)
     
     ## 展望
     
    diff --git a/YaeBlog/source/posts/2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.jpg b/YaeBlog/source/posts/2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.jpg
    deleted file mode 100644
    index cc4461f..0000000
    --- a/YaeBlog/source/posts/2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.jpg
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:9a8ba878ab36ef4d9e540be7e8ee83e5c5d980e3968f5c7bea2917fded6ff83d
    -size 83539
    diff --git a/YaeBlog/source/posts/2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.webp b/YaeBlog/source/posts/2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.webp
    new file mode 100644
    index 0000000..68361ff
    --- /dev/null
    +++ b/YaeBlog/source/posts/2022-final/2022-12-30-14-26-19-QQ_Image_1672381538441.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:6e697061aab4e88f7b3eae2b91f9d2c9a9291da95d54f5bd4bfc0a1f6825cc9f
    +size 249208
    diff --git a/YaeBlog/source/posts/2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.jpg b/YaeBlog/source/posts/2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.jpg
    deleted file mode 100644
    index af7e125..0000000
    --- a/YaeBlog/source/posts/2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.jpg
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:7249babd4c5bdf499db30f92c20f18d4ff4fb4f0f7c2eff3afbf33bf8a37abe2
    -size 122345
    diff --git a/YaeBlog/source/posts/2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.webp b/YaeBlog/source/posts/2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.webp
    new file mode 100644
    index 0000000..d3fa901
    --- /dev/null
    +++ b/YaeBlog/source/posts/2022-final/2022-12-30-14-28-12-QQ_Image_1672381543836.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:f663fe26e66a2498f12b0544993279d1458e165dde9c4c9046fc77d622ef5a9d
    +size 768344
    diff --git a/YaeBlog/source/posts/2022-summer-vacation.md b/YaeBlog/source/posts/2022-summer-vacation.md
    index 39f9d13..3b53c96 100644
    --- a/YaeBlog/source/posts/2022-summer-vacation.md
    +++ b/YaeBlog/source/posts/2022-summer-vacation.md
    @@ -1,11 +1,11 @@
     ---
     title: 2022年暑假碎碎念
    +date: 2022-08-22T15:39:13.0000000
     tags:
    -  - 杂谈
    -typora-root-url: 2022-summer-vacation
    -date: 2022-08-22 15:39:13
    +- 杂谈
     ---
     
    +
     在8个月的漫长寒假的最后两个月,~~也就是俗称的暑假中~~,我都干了些什么?
     
     
    @@ -32,7 +32,7 @@ date: 2022-08-22 15:39:13
     - 下定决定要参加下一学期的物理竞赛,但是在听了讲座之后直接决定开学再开始学习,~~我知道我在家没法学习,俗称开摆~~
     - 又捡起了`Blender`,并在[Github](https://github.com/tanjian1998/bupt_minecraft)上找到了伟大的前辈们在`Minecraft`里复刻的老校区,希望能用`Blender`渲染几张图当作桌面。
     
    -![唯一的一张成品](result1.png)
    +![唯一的一张成品](result1.webp)
     
     > 在此感谢所有为此付出过汗水的前辈们,让我这个即将搬入老校区的萌新能提前一睹老校区的风采。
     
    diff --git a/YaeBlog/source/posts/2022-summer-vacation/result1.png b/YaeBlog/source/posts/2022-summer-vacation/result1.png
    deleted file mode 100644
    index 2d6effc..0000000
    --- a/YaeBlog/source/posts/2022-summer-vacation/result1.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:e69a4cb12a942e16e289babf4c9448ae91725f66915501fdb300924c5c436c9b
    -size 3225055
    diff --git a/YaeBlog/source/posts/2022-summer-vacation/result1.webp b/YaeBlog/source/posts/2022-summer-vacation/result1.webp
    new file mode 100644
    index 0000000..91b8e78
    --- /dev/null
    +++ b/YaeBlog/source/posts/2022-summer-vacation/result1.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:15a99f1ad3aa6b4edb7d3567b7f2dafb287ff457c0492618e6c37df0cde3cc43
    +size 137362
    diff --git a/YaeBlog/source/posts/2023-final.md b/YaeBlog/source/posts/2023-final.md
    index 312097a..b06d0e6 100644
    --- a/YaeBlog/source/posts/2023-final.md
    +++ b/YaeBlog/source/posts/2023-final.md
    @@ -1,11 +1,12 @@
     ---
     title: 2023年年终总结
    +date: 2024-02-29T20:18:19.0000000
     tags:
    -  - 杂谈
    -  - 年终总结
    -date: 2024-2-29 20:18:19
    +- 杂谈
    +- 年终总结
     ---
     
    +
     虽然2023年已经过去了两个月,但是年终总结还是要发的。
     
     
    @@ -44,7 +45,7 @@ date: 2024-2-29 20:18:19
     
     2023年最令我吃惊的事情是我刷B站的时长:
     
    -![image-20240303165826486](2023-final/image-20240303165826486.png)
    +![image-20240303165826486](2023-final/image-20240303165826486.webp)
     
     容易计算得出,我一共看了64天的B站,接近六分之一的时间都在看。虽然我确实有着在干活的时候黑听B站和把B站当作音乐播放器的习惯,但是这个时间未免有点太长了。下一年一定要在这个方面做出一定的改变,将更多的时间放在看书上面去,~~虽然写这句话的时候我就在黑听B站~~。
     
    diff --git a/YaeBlog/source/posts/2023-final/image-20240303165826486.png b/YaeBlog/source/posts/2023-final/image-20240303165826486.png
    deleted file mode 100644
    index 0d82809..0000000
    --- a/YaeBlog/source/posts/2023-final/image-20240303165826486.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:92bf121700d13fc738d5bcf46d9a814629dbf16b7ca289433506f6e8af70396c
    -size 118808
    diff --git a/YaeBlog/source/posts/2023-final/image-20240303165826486.webp b/YaeBlog/source/posts/2023-final/image-20240303165826486.webp
    new file mode 100644
    index 0000000..43289ac
    --- /dev/null
    +++ b/YaeBlog/source/posts/2023-final/image-20240303165826486.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:614e92e6c52bbfc1e87811dd0c283abe1da70cee41e143b4398cff0a6bacdad2
    +size 72936
    diff --git a/YaeBlog/source/posts/2024-final.md b/YaeBlog/source/posts/2024-final.md
    index 1e12ad1..56411f3 100644
    --- a/YaeBlog/source/posts/2024-final.md
    +++ b/YaeBlog/source/posts/2024-final.md
    @@ -6,6 +6,7 @@ tags:
     - 年终总结
     ---
     
    +
     欸,年终总结难道不是应该在新年当天发出吗,什么已经是新年第三天了?!
     
     然而年末偶遇流感病毒,头疼脑热强如怪物,拼尽全力也无法战胜。
    @@ -70,7 +71,7 @@ tags:
     
     不过我的B站观看时长再度增长30%,这好吗,这不好,~~有这么多时间刷B站,鬼知道你匆匆在哪了~~。
     
    -![image-20250115171809775](./2024-final/image-20250115171809775.png)
    +![image-20250115171809775](./2024-final/image-20250115171809775.webp)
     
     ### 未来
     
    diff --git a/YaeBlog/source/posts/2024-final/image-20250115171809775.png b/YaeBlog/source/posts/2024-final/image-20250115171809775.png
    deleted file mode 100644
    index d54b6f4..0000000
    --- a/YaeBlog/source/posts/2024-final/image-20250115171809775.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:6904fe34bcf46c1e9d672f278cedd54a59638cf2ea8d5a909acee9b436955387
    -size 633715
    diff --git a/YaeBlog/source/posts/2024-final/image-20250115171809775.webp b/YaeBlog/source/posts/2024-final/image-20250115171809775.webp
    new file mode 100644
    index 0000000..74fdb8b
    --- /dev/null
    +++ b/YaeBlog/source/posts/2024-final/image-20250115171809775.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:c89ea64941bb737a304acca227436c41cb0eb9e0764a33d967888135265ccddd
    +size 38728
    diff --git a/YaeBlog/source/posts/big-homework.md b/YaeBlog/source/posts/big-homework.md
    index d763876..6f8b801 100644
    --- a/YaeBlog/source/posts/big-homework.md
    +++ b/YaeBlog/source/posts/big-homework.md
    @@ -1,11 +1,11 @@
     ---
     title: 人生代码大作业初体验
    +date: 2022-07-27T11:34:49.0000000
     tags:
    -  - 杂谈
    -typora-root-url: big-homework
    -date: 2022-07-27 11:34:49
    +- 杂谈
     ---
     
    +
     在大学也呆了一年了,终于遇上了第一个需要多人合作的写代码项目。从四月底分组完成,任务部署下来到七月初接近尾声,在这两个多月的时间里,也算是经历了不少,学到了不少。
     
     
    @@ -44,7 +44,7 @@ date: 2022-07-27 11:34:49
     
     而且采用 `Git`还有一个好处,采用 `Github`的 `Insight`功能可以轻松的看出大家的贡献值()。
     
    -![img](1.png)
    +![img](1.webp)
     
     ## 一些技术上的收获
     
    diff --git a/YaeBlog/source/posts/big-homework/1.png b/YaeBlog/source/posts/big-homework/1.png
    deleted file mode 100644
    index d062e81..0000000
    --- a/YaeBlog/source/posts/big-homework/1.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:2d6244372d81d16899a7536cbd98dfd31c448ada89b899cd302738e3946d7544
    -size 43864
    diff --git a/YaeBlog/source/posts/big-homework/1.webp b/YaeBlog/source/posts/big-homework/1.webp
    new file mode 100644
    index 0000000..21ce881
    --- /dev/null
    +++ b/YaeBlog/source/posts/big-homework/1.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:f7b9d0cee70e8310ba9a7e0c771bea6e8eb62714fa37c4cce98bb9c7db7fc9f5
    +size 19390
    diff --git a/YaeBlog/source/posts/build-blog-record.md b/YaeBlog/source/posts/build-blog-record.md
    index 248b787..ec9ba27 100644
    --- a/YaeBlog/source/posts/build-blog-record.md
    +++ b/YaeBlog/source/posts/build-blog-record.md
    @@ -1,12 +1,12 @@
     ---
     title: 建立博客过程的记录
    -typora-root-url: 建立博客过程的记录
    -date: 2022-04-08 11:52:32
    +date: 2022-04-08T11:52:32.0000000
     tags:
    -    - 技术笔记
    +- 技术笔记
     ---
     
     
    +
     当我已经在Python的浩瀚大海遨(zheng)游(zha)了半个暑假后,我决定尝试一下传说中程序员专用的学(zhuang)习(bi)手(fangfa)段(fa)——建立自己的个人博客。作为一个半懂不懂的Python程序员,心中冒出的第一个想法自然是采用Python的Django作为开发自己的个人博客的手段。然而,在阅读了[用Django搭建个人博客](https://www.dusaiphoto.com/article/2/)等的其他人搭建这类动态博客的过程记录之后,我便义无反顾的转向了采用javascript开发的博客框架[Hexo](https://hexo.io),说好的Python信仰呢。无他,唯简单尔。
     
     
    @@ -131,7 +131,7 @@ Hexo init blog
     ```
     Hexo会以blog为名称创建一个博客文件夹,这个文件夹的内容为
     
    -![文件夹截图](1.png)
    +![文件夹截图](1.webp)
     
     `node_modules`文件夹是Hexo需要用到的一些npm依赖包的存放地址,`public`文件夹下是由Hexo渲染产生的静态博客文件,`scaffolds`文件夹是博客用到的模板文件,在默认情况下应该有`draft.md`,`page.md`,`post.md`三个模板文件。`themes`是Hexo中可以使用的主题文件。主题也是Hexo一个非常方便的设计,我们可以方便使用其他人编写的Hexo Themes,让自己的博客在不同的风格之间变换。`source`文件夹就是存放我们写作的博客的地方。一般这里面会有两个子文件夹,`_draft`, `_posts`。我们在里面在创建一个`img`文件夹,把自己的头像图片和网站的图标文件都放在里面,在之后的设置的时候使用。
     
    @@ -146,7 +146,7 @@ INFO  Hexo is running at http://localhost:4000/ . Press Ctrl+C to stop.
     
     会在本地运行Hexo自带的一台静态博客服务器。我们用浏览器访问http://localhost:4000, 就可以看见Hexo博客的初始界面
     
    -![初始截图](2.png)
    +![初始截图](2.webp)
     
     这便说明安装成功了,~~可以开香槟了~~
     
    diff --git a/YaeBlog/source/posts/build-blog-record/1.png b/YaeBlog/source/posts/build-blog-record/1.png
    deleted file mode 100644
    index 504bdb6..0000000
    --- a/YaeBlog/source/posts/build-blog-record/1.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:43c5d93f5f893183366f51c021a5544d973ef8ff1d2ad59c05ad6dc8af6b5255
    -size 45244
    diff --git a/YaeBlog/source/posts/build-blog-record/1.webp b/YaeBlog/source/posts/build-blog-record/1.webp
    new file mode 100644
    index 0000000..b72db8a
    --- /dev/null
    +++ b/YaeBlog/source/posts/build-blog-record/1.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:a11c506a9360640034aa30fd9d6204d273f0fe7b707516a5269d28b79bacff81
    +size 9358
    diff --git a/YaeBlog/source/posts/build-blog-record/2.png b/YaeBlog/source/posts/build-blog-record/2.png
    deleted file mode 100644
    index aa05058..0000000
    --- a/YaeBlog/source/posts/build-blog-record/2.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:d94c3bcc5c7838b4c12a28b96a212910101ee616dd9f581f77a33775ae29aae8
    -size 494302
    diff --git a/YaeBlog/source/posts/build-blog-record/2.webp b/YaeBlog/source/posts/build-blog-record/2.webp
    new file mode 100644
    index 0000000..d5c9269
    --- /dev/null
    +++ b/YaeBlog/source/posts/build-blog-record/2.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:9dbd874b7763d86ccf67228ba263cc57d048241497ad91ed4bac50fd63f7ed34
    +size 39286
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source.md b/YaeBlog/source/posts/build-dotnet-from-source.md
    index 200a665..87fd851 100644
    --- a/YaeBlog/source/posts/build-dotnet-from-source.md
    +++ b/YaeBlog/source/posts/build-dotnet-from-source.md
    @@ -7,6 +7,7 @@ tags:
     ---
     
     
    +
     我们编译是这样的,在本平台上编译只要敲三条命令就好了,而交叉编译要考虑的就很多了。
     
     
    @@ -45,7 +46,7 @@ tags:
     
     通常一份GNU工具链只能针对一个平台进行编译,但是LLVM工具链是一套先天的交叉编译工具链,例如对于`llc`工具,使用`llc --version`命令可以看见该编译器可以生成多种目标平台上的汇编代码:
     
    -![image-20240824120646587](./build-dotnet-from-source/image-20240824120646587.png)
    +![image-20240824120646587](./build-dotnet-from-source/image-20240824120646587.webp)
     
     在使用`clang++`时加上`--target=`指定目标三元组就可以进行交叉编译。
     
    @@ -62,7 +63,7 @@ int main()
     }
     ```
     
    -![image-20240824121425007](./build-dotnet-from-source/image-20240824121425007.png)
    +![image-20240824121425007](./build-dotnet-from-source/image-20240824121425007.webp)
     
     看样子交叉编译也不是开箱即用的。最开始我们猜想系统提供的LLVM工具链没有被配置为交叉编译,因此尝试在本地自行编译一套LLVM工具链。
     
    @@ -81,7 +82,7 @@ cmake ../llvm-project.src/llvm \
     
     编译之后的成果会安装到`/usr/local/`目录下,而在`$PATH`环境变量中`/usr/local`位置将在`/usr`目录之前,因此调用时将会优先调用我们自行编译的LLVM工具链,而不是系统中安装的LLVM工具链。
     
    -![image-20240824134158262](./build-dotnet-from-source/image-20240824134158262.png)
    +![image-20240824134158262](./build-dotnet-from-source/image-20240824134158262.webp)
     
     但是使用这套编译工具链仍然会爆出和之前一样的问题。说明这并不是系统安装LLVM工具链的问题。仔细一想也确实,这里提示找不到对应的头文件应该是找不到RISC-V架构之下的头文件——这里的也是交叉编译的主要问题所在:虽然LLVM工具链宣称自己是原生支持交叉编译的,但是没人宣称说标准库和头文件是原生的。这里我们就需要一个根文件系统来提供这些头文件和各种库文件。
     
    @@ -198,7 +199,7 @@ clang++ --target=riscv64-linux-gnu --sysroot=$ROOTFS_DIR -fuse-ld=lld hello.cpp
     
     第一个问题的回答是Arch Linux安装的LLVM工具是可以交叉编译的。虽然在Arch Linux官方构建LLVM工具链的[构建脚本](https://gitlab.archlinux.org/archlinux/packaging/packages/clang/-/blob/main/PKGBUILD?ref_type=heads)中没有使用`LLVM_TARGETS_TO_BUILD`参数,但是这个参数的默认值是`all`。这一点我们也可以通过实验来验证。
     
    -![image-20240824153514149](./build-dotnet-from-source/image-20240824153514149.png)于是回到编译`llvm`的目录下执行`cat install_manifest.txt | sudo xargs rm`。
    +![image-20240824153514149](./build-dotnet-from-source/image-20240824153514149.webp)于是回到编译`llvm`的目录下执行`cat install_manifest.txt | sudo xargs rm`。
     
     第二个问题的回答可以使用实验来验证,首先安装`riscv64-linux-gnu-gcc`,然后将根文件系统的位置设置为`/usr/riscv64-linux-gnu`,重新编译上面的你好世界样例。编译之后可以正常执行。
     
    @@ -229,4 +230,4 @@ export ROOTFS_DIR=
     
     但是现在的.NET在RISC-V平台上还是废物一个,甚至连`dotnet new`都跑不过,下一步看看能不能运行一下运行时的测试集看看。
     
    -![image-20240824214145759](./build-dotnet-from-source/image-20240824214145759.png)
    +![image-20240824214145759](./build-dotnet-from-source/image-20240824214145759.webp)
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824120646587.png b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824120646587.png
    deleted file mode 100644
    index a28a10c..0000000
    --- a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824120646587.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:df8608331d9104540c58c3710c96d95d6d569f248ec87a9a2ac13e3a05a54365
    -size 189748
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824120646587.webp b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824120646587.webp
    new file mode 100644
    index 0000000..053a39b
    --- /dev/null
    +++ b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824120646587.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:1a1255246ecef4fd56e7e8053bf83cab43c8392b2baa6a7519742dc880d6ba73
    +size 80142
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824121425007.png b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824121425007.png
    deleted file mode 100644
    index 94f9a09..0000000
    --- a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824121425007.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:80b04aa95272ee4a22466a1168ff6dff4343cde50deb97d3a3ad4fcc7ed33793
    -size 27785
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824121425007.webp b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824121425007.webp
    new file mode 100644
    index 0000000..d86de6a
    --- /dev/null
    +++ b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824121425007.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:4770f52360bdf2c08ca7cdaf6a27ec2e5b7ea43da160666ad85e3a15b63a629b
    +size 17044
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824134158262.png b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824134158262.png
    deleted file mode 100644
    index 651f00b..0000000
    --- a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824134158262.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:8c76e238cf1aefe3033f7e6a15b41c09ee323d63397d670ef35efadb4cf010a0
    -size 149831
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824134158262.webp b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824134158262.webp
    new file mode 100644
    index 0000000..e558fa7
    --- /dev/null
    +++ b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824134158262.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:a96043307024619a028381259714d9fbaa3c86e858d6085c9c525de492ac4b3f
    +size 73898
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824153514149.png b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824153514149.png
    deleted file mode 100644
    index 4e84f2f..0000000
    --- a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824153514149.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:0403a9fa66547b6b597ff9ce568bcc6b2c45f57f83499ab14cf1c4405dfde730
    -size 30117
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824153514149.webp b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824153514149.webp
    new file mode 100644
    index 0000000..a987a1c
    --- /dev/null
    +++ b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824153514149.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:7bcec03c67fb5f8552a6884eed38ad4d709ea0d0be60b945030744ed809b7d6b
    +size 18058
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824214145759.png b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824214145759.png
    deleted file mode 100644
    index 2ca0b45..0000000
    --- a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824214145759.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:632da4d01c561cb4ec27e6130e3754b6ed82f6e212d6e6f2fefae1d115951419
    -size 123971
    diff --git a/YaeBlog/source/posts/build-dotnet-from-source/image-20240824214145759.webp b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824214145759.webp
    new file mode 100644
    index 0000000..5b6bdbc
    --- /dev/null
    +++ b/YaeBlog/source/posts/build-dotnet-from-source/image-20240824214145759.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:1bf8710c2b558f1d33d6adbf294c31f82773453fcb2bcb6d7f062b8ca90c2bd1
    +size 33928
    diff --git a/YaeBlog/source/posts/c-include-problems.md b/YaeBlog/source/posts/c-include-problems.md
    index 5993d12..6839ac3 100644
    --- a/YaeBlog/source/posts/c-include-problems.md
    +++ b/YaeBlog/source/posts/c-include-problems.md
    @@ -1,12 +1,12 @@
     ---
     title: C项目中有关头文件的一些问题
    +date: 2022-05-08T11:35:19.0000000
     tags:
    -  - 技术笔记
    -  - C/C++
    -typora-root-url: c-include-problems
    -date: 2022-05-08 11:35:19
    +- 技术笔记
    +- C/C++
     ---
     
    +
     最近在完成一门`C`语言课程的大作业,课设老师要求我们将程序分模块的开发。在编写项目头文件的时候,遇到了一些令本菜鸡大开眼界的问题。
     
     
    @@ -17,7 +17,7 @@ date: 2022-05-08 11:35:19
     
     我项目的结构大致如图所示:
     
    -![](1.png)
    +![](1.webp)
     
     在`include`的头文件目录下有两个头文件,`rail.h`和`bus.h`,这两个头文件分别定义了两个结构体`rail_node_t`和`bus_t`。
     
    @@ -68,7 +68,7 @@ typedef struct bus bus_t;
     
     项目的`test`文件夹下是单元测试文件夹,但是在编译的时候会报错
     
    -![](2.png)
    +![](2.webp)
     
     大意就是在一个google test内部的头文件中有几个函数找不到定义,这个函数都位于`io.h`这个头文件中。
     
    diff --git a/YaeBlog/source/posts/c-include-problems/1.png b/YaeBlog/source/posts/c-include-problems/1.png
    deleted file mode 100644
    index db120ad..0000000
    --- a/YaeBlog/source/posts/c-include-problems/1.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:401d690fe4ac6711decb03bfa27bf35d2045960b2f7bfdbb6964fc9bb0320a50
    -size 11997
    diff --git a/YaeBlog/source/posts/c-include-problems/1.webp b/YaeBlog/source/posts/c-include-problems/1.webp
    new file mode 100644
    index 0000000..59798b8
    --- /dev/null
    +++ b/YaeBlog/source/posts/c-include-problems/1.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:eebdf09480fdcf51b5d110c0f1a78649df4bae999dd8151c2a4bf49333ef7e4a
    +size 4450
    diff --git a/YaeBlog/source/posts/c-include-problems/2.png b/YaeBlog/source/posts/c-include-problems/2.png
    deleted file mode 100644
    index 4db5f26..0000000
    --- a/YaeBlog/source/posts/c-include-problems/2.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:865e75431d84961d39bf653598807c82c09209b7236528dc91747b49fb41c361
    -size 1538223
    diff --git a/YaeBlog/source/posts/c-include-problems/2.webp b/YaeBlog/source/posts/c-include-problems/2.webp
    new file mode 100644
    index 0000000..c40a685
    --- /dev/null
    +++ b/YaeBlog/source/posts/c-include-problems/2.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:779960f8c829262183e2fdce6888b9f25dce625b60862d925d79195830a4e7b4
    +size 210008
    diff --git a/YaeBlog/source/posts/cncc-2024.md b/YaeBlog/source/posts/cncc-2024.md
    index 42d79d9..3a0e19f 100644
    --- a/YaeBlog/source/posts/cncc-2024.md
    +++ b/YaeBlog/source/posts/cncc-2024.md
    @@ -5,13 +5,14 @@ tags:
     - 杂谈
     ---
     
    +
     2024年的中国计算机大会于10月24日到10月26日在浙江省金华市东阳市横店镇举办,而鄙人在下不才我,有幸受到实验室资助前去参观学习。
     
     
     
     首先开幕式镇楼。
     
    -![image-20241102212738598](./cncc-2024/image-20241102212738598.png)
    +![image-20241102212738598](./cncc-2024/image-20241102212738598.webp)
     
     ## 学术上
     
    @@ -21,11 +22,11 @@ tags:
     
     第一个报告是华为庞加莱实验室秦彬娟老师的《异构智算时代的操作系统演进》。报告高屋建瓴,从比较宏观的角度上介绍了当前异构融合操作系统诞生的背景、发展的方向。在报告中重点介绍了一种异构融合操作系统的设计思路:通过三层架构,基于互联池化技术,构建AI时代的融合算力系统。系统中的三层包括:(1)池化基础底层,包括多设备的融合和池化设备虚拟化;(2)异构融合核心子系统,例如异构融合调度系统、异构融合内存和异构融合存储系统;(3)异构核心服务。总的来说,这个报告在一定程度上勾勒出了未来一个异构融合操作系统应有的各项功能,但是显然这一操作系统的实现还存在着明显的困难。
     
    -![image-20241102211959206](./cncc-2024/image-20241102211959206.png)
    +![image-20241102211959206](./cncc-2024/image-20241102211959206.webp)
     
     下面一个报告是较为有干货的报告,北京航空航天大学刘瀚骋老师的《异构融合OS及多样性内存管理框架》。报告中介绍了一个称作`FMMU`的系统,是对于异构融合操作系统中内存管理系统的探索。报告中首先介绍了内存池化技术对于异构融合操作系统的重要性,指出分布式共享内存(Distributed Shared Memory)可能是实现内存池化技术的未来。然后介绍了将部分内存管理中的计算卸载到可编程网络硬件中来加速分布式内存访问的新思路。最后在报告中提到了内存管理技术如何解决错误预测和错误回复的问题。虽然在听的时候没太注意,但是现在总结的时候才发现这个报告的思路似乎有点混乱,尤其是最后一点和内存管理系统并没有什么直接的关系,而且这个内存管理系统似乎不是**异构系统**的内存管理,反而是分布式系统的内存管理。不过总的来说,这个报告还是非常实际的,介绍了不少当前异构融合操作系统中的内存管理面临的问题和解决问题的探索。
     
    -![image-20241102212355390](./cncc-2024/image-20241102212355390.png)
    +![image-20241102212355390](./cncc-2024/image-20241102212355390.webp)
     
     第三个报告是国防科技大学李东升老师的《异构计算环境下的分布式深度学习训练》。报告首先从李老师的主业——并行计算起手,介绍了深度学习训练过程中主要的各种并行方法,例如数据并行、模型并行和混合并行等,指出目前大模型的并行训练存在着计算/存储/通信难的问题。因此,提出了一个智能模型训练并行任务划分方法:(1)基于符号算子的计算图定义方法;(2)面向Transformer模型的流水线并行任务划分方法;(3)异构资源感知的流水线并行任务划分方法。然后针对分布式模型训练中通信调度存在的通信墙、数据依赖关系复杂等的问题,提出综合词嵌入表的稀疏通信调度技术、流水线并行的P2P通信调度技术、模型计算的统一操作执行引擎和网络链路感知的通信执行引擎的通信调度技术。最后提到了智能模型训练 的内存优化技术,针对现有重计算技术(re-computing)和存储交换(swapping)技术存在的问题,提出了一种面向大型智能模型训练的细粒度内存优化方法`DELTA`。
     
    @@ -49,7 +50,7 @@ Plane讨论没有参加。
     
     第二个报告是南京大学冯新宇老师的《基于仓颉语言的嵌入式DSL开发》,同时冯新宇老师也是仓颉语言的首席架构师。冯老师的这个报告主要聚焦于仓颉语言提供的嵌入式DSL能力,而嵌入式DSL这一设计范式已经在前端开发中展现了不俗的潜力。报告中介绍了嵌入式DSL出现的背景,仓颉中为了提供嵌入式DSL而引入的语法糖、仓颉提供的嵌入式DSL工具箱等。虽然仓颉语言是一个主要面向上层应用开发的语言,但是仓颉中丰富的DSL能力还是给异构编程模型的设计提供了不少的启发。而且目前在各种深度学习编译器中DSL的应用也非常广泛,例如`triton`。
     
    -![image-20241102212536635](./cncc-2024/image-20241102212536635.png)
    +![image-20241102212536635](./cncc-2024/image-20241102212536635.webp)
     
     第三个报告是在存算一体的芯片上做数据库的加速,第四个报告是OpenHarmony上`ArkTS`程序的静态分析,都没怎么听。
     
    diff --git a/YaeBlog/source/posts/cncc-2024/image-20241102211959206.png b/YaeBlog/source/posts/cncc-2024/image-20241102211959206.png
    deleted file mode 100644
    index 01a6ff9..0000000
    --- a/YaeBlog/source/posts/cncc-2024/image-20241102211959206.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:76119fc2c4cd4f53679f800c41780727ef53a14bdefc594156d177cc49cb6bbb
    -size 2408090
    diff --git a/YaeBlog/source/posts/cncc-2024/image-20241102211959206.webp b/YaeBlog/source/posts/cncc-2024/image-20241102211959206.webp
    new file mode 100644
    index 0000000..d0fa3bb
    --- /dev/null
    +++ b/YaeBlog/source/posts/cncc-2024/image-20241102211959206.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:c57e583d8087b8f42c15cc8b351d8053af6f7a5a00179b846bcdfae9f68fc3a0
    +size 139100
    diff --git a/YaeBlog/source/posts/cncc-2024/image-20241102212355390.png b/YaeBlog/source/posts/cncc-2024/image-20241102212355390.png
    deleted file mode 100644
    index adc38ef..0000000
    --- a/YaeBlog/source/posts/cncc-2024/image-20241102212355390.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:9b30674b16f2d756eb8df19d33d2de459c194a8f1fb3bad99278adfa5c31e0dd
    -size 2712347
    diff --git a/YaeBlog/source/posts/cncc-2024/image-20241102212355390.webp b/YaeBlog/source/posts/cncc-2024/image-20241102212355390.webp
    new file mode 100644
    index 0000000..08402fc
    --- /dev/null
    +++ b/YaeBlog/source/posts/cncc-2024/image-20241102212355390.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:4635d95dc1024cba26c8fee048df2efeb531ab2480d9179456c1919ec2d62c9a
    +size 243730
    diff --git a/YaeBlog/source/posts/cncc-2024/image-20241102212536635.png b/YaeBlog/source/posts/cncc-2024/image-20241102212536635.png
    deleted file mode 100644
    index 475ac3e..0000000
    --- a/YaeBlog/source/posts/cncc-2024/image-20241102212536635.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:43e1c4a59ef2a75b57ce30d0ab780d2f381e74531233c648683bbd2f20136ddf
    -size 1544073
    diff --git a/YaeBlog/source/posts/cncc-2024/image-20241102212536635.webp b/YaeBlog/source/posts/cncc-2024/image-20241102212536635.webp
    new file mode 100644
    index 0000000..f050150
    --- /dev/null
    +++ b/YaeBlog/source/posts/cncc-2024/image-20241102212536635.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:df0984d43f790ca38a0c42560b450fc68cd21ac5827b4238882798c21425ea31
    +size 48104
    diff --git a/YaeBlog/source/posts/cncc-2024/image-20241102212738598.png b/YaeBlog/source/posts/cncc-2024/image-20241102212738598.png
    deleted file mode 100644
    index 5fda1a0..0000000
    --- a/YaeBlog/source/posts/cncc-2024/image-20241102212738598.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:2b03bf1455dd59785e21e88c5a106f040154f23977c968a68d5f9341e82fb5a1
    -size 8559543
    diff --git a/YaeBlog/source/posts/cncc-2024/image-20241102212738598.webp b/YaeBlog/source/posts/cncc-2024/image-20241102212738598.webp
    new file mode 100644
    index 0000000..b2ca998
    --- /dev/null
    +++ b/YaeBlog/source/posts/cncc-2024/image-20241102212738598.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:82131843f7a9c6c402015d3d7f81dc63f6671a9189d02bd60f0f668ec21b74ca
    +size 654436
    diff --git a/YaeBlog/source/posts/compile-mediapipe.md b/YaeBlog/source/posts/compile-mediapipe.md
    index 56efebb..e1bb692 100644
    --- a/YaeBlog/source/posts/compile-mediapipe.md
    +++ b/YaeBlog/source/posts/compile-mediapipe.md
    @@ -1,11 +1,12 @@
     ---
     title: 编译MediaPipe框架
    +date: 2022-11-11T22:20:25.0000000
     tags:
    -  - C/C++
    -  - 技术笔记
    -date: 2022-11-11 22:20:25
    +- C/C++
    +- 技术笔记
     ---
     
    +
     编译MediaPipe框架。
     
     
    @@ -198,7 +199,7 @@ bazel build -c opt --strip=ALWAYS \
     
     如果在编译的过程中提示缺失`dx.jar`这个文件而且你用的SDK版本还是高于31的,那可能是SDK中缺失了这个文件,可以将SDk降级到30就含有这个文件了。我使用的解决办法比较离奇,我是将30版本的SDK文件中的这个文件软链接过来,解决了这个问题。
     
    -![](compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.png)
    +![](compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.webp)
     
     编译消耗的时间可能比较的长,耐心等待即可。
     
    @@ -227,7 +228,7 @@ bazel build -c opt //mediapipe/graphs/pose_tracking:pose_tracking_gpu_binary_gra
     
     然后还需要从服务器上下载`tflite`文件,`Pose Tracking`这个解决方案需要两个`tflite`文件,第一个是[pose_detection.tflite](https://storage.googleapis.com/mediapipe-assets/pose_detection.tflite),第二个文件则有三个不同的选择,分别对于解决方案中提供的三个质量版本:
     
    -![](compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.png)
    +![](compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.webp)
     
     下载地址是[pose_landmark_full.tflite](https://storage.googleapis.com/mediapipe-assets/pose_landmark_full.tflite),[pose_landmark_heavy.tflite](https://storage.googleapis.com/mediapipe-assets/pose_landmark_heavy.tflite)和[pose_landmark_lite.tflite](https://storage.googleapis.com/mediapipe-assets/pose_landmark_lite.tflite)。
     
    diff --git a/YaeBlog/source/posts/compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.png b/YaeBlog/source/posts/compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.png
    deleted file mode 100644
    index b808741..0000000
    --- a/YaeBlog/source/posts/compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:b2a53c78a0e68f31fe6a2711de630db64aa1b7d81ca637802103a45102233d99
    -size 99702
    diff --git a/YaeBlog/source/posts/compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.webp b/YaeBlog/source/posts/compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.webp
    new file mode 100644
    index 0000000..aaf12b3
    --- /dev/null
    +++ b/YaeBlog/source/posts/compile-mediapipe/2023-01-15-22-05-41-Screenshot_20230115_220521.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:f5a965740f47cf499c950b32ce8f0b9acdb6a6f47210e0f31a9f723173e57b6b
    +size 50502
    diff --git a/YaeBlog/source/posts/compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.png b/YaeBlog/source/posts/compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.png
    deleted file mode 100644
    index 1fbd658..0000000
    --- a/YaeBlog/source/posts/compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:15b1e6bc0d87f46937650d98703403b0536a7a61f30477418ae03a8b6361c252
    -size 26745
    diff --git a/YaeBlog/source/posts/compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.webp b/YaeBlog/source/posts/compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.webp
    new file mode 100644
    index 0000000..5429353
    --- /dev/null
    +++ b/YaeBlog/source/posts/compile-mediapipe/2023-01-19-20-20-40-Screenshot_20230119_202008.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:819ba78fcbd2115d7f85b54c2eab6f4bf7ed3990c8e5546163c8769b03321ac0
    +size 7640
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline.md b/YaeBlog/source/posts/computer-architecture-pipeline.md
    index 6228525..06176f5 100644
    --- a/YaeBlog/source/posts/computer-architecture-pipeline.md
    +++ b/YaeBlog/source/posts/computer-architecture-pipeline.md
    @@ -1,20 +1,21 @@
     ---
     title: 计算机系统结构——流水线复习
    +date: 2024-06-12T20:27:25.0000000
     tags:
    -  - 计算机系统结构
    -  - 学习资料
    -date: 2024-06-12 20:27:25
    +- 计算机系统结构
    +- 学习资料
     ---
     
    +
     让指令的各个执行阶段依次进行运行是一个简单而自然的想法,但是这种方式执行速度慢、运行效率低。因此一个很自然的想法就是将指令重叠起来运行,让执行功能部件被充分的利用起来,这就是**流水线**。
     
     流水线的表示方法有两种。
     
    -![image-20240612184855300](computer-architecture-pipeline/image-20240612184855300.png)
    +![image-20240612184855300](computer-architecture-pipeline/image-20240612184855300.webp)
     
     第一种被称作**连接图**,清晰的表达出了流水线内部的逻辑关系。
     
    -![image-20240612184949777](computer-architecture-pipeline/image-20240612184949777.png)
    +![image-20240612184949777](computer-architecture-pipeline/image-20240612184949777.webp)
     
     > 上图中给出了两个流水线中的概念:通过时间和排空时间。其中通过时间又被称作装入时间,是指第一个任务进入流水线到完成的事件;排空时间则相反,是最后一个任务通过流水线的时间。
     
    @@ -40,7 +41,7 @@ date: 2024-06-12 20:27:25
     - 静态流水线,同一时间内,多功能流水线的各段只能按照同一种功能的方式连接。
     - 动态流水线,同一时间内,多功能流水线的各种可以按照不同的方式连接,执行不同的功能。
     
    -![image-20240612190426368](computer-architecture-pipeline/image-20240612190426368.png)
    +![image-20240612190426368](computer-architecture-pipeline/image-20240612190426368.webp)
     
     按照流水线中是否存在反馈回路分类:
     
    @@ -58,7 +59,7 @@ date: 2024-06-12 20:27:25
     - 加速比,同一任务,不使用流水线所使用时间与使用流水线所用时间比。
     - 效率,流水线设备的利用率。
     
    -![image-20240612192700169](computer-architecture-pipeline/image-20240612192700169.png)
    +![image-20240612192700169](computer-architecture-pipeline/image-20240612192700169.webp)
     
     在设计流水线的过程中存在若干问题。
     
    @@ -68,7 +69,7 @@ date: 2024-06-12 20:27:25
     
     一个典型的五段流水线MIPS流水线:
     
    -![image-20240612193301372](computer-architecture-pipeline/image-20240612193301372.png)
    +![image-20240612193301372](computer-architecture-pipeline/image-20240612193301372.webp)
     
     
     
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184855300.png b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184855300.png
    deleted file mode 100644
    index aa9d483..0000000
    --- a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184855300.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:852fa4b226a15ff9e1f34cf13ef37426f4d6b7f37b0123150c99a76ec437c024
    -size 23656
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184855300.webp b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184855300.webp
    new file mode 100644
    index 0000000..4e7975e
    --- /dev/null
    +++ b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184855300.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:10f512f3ccd787ac8168ad92d37fdc388dd2d0a913e5f8f0a660be6f6d7fe3d0
    +size 9974
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184949777.png b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184949777.png
    deleted file mode 100644
    index 8af2134..0000000
    --- a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184949777.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:2ba0e36747d52eba53e756bac857f19da8c1859aba76c1819142940f6128e82a
    -size 75273
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184949777.webp b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184949777.webp
    new file mode 100644
    index 0000000..b3fc8fb
    --- /dev/null
    +++ b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612184949777.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:5099ed84c349c7694e281f1443f377cb3ab2006b8dca93fd4a9b9fbacfaf554f
    +size 35682
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612190426368.png b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612190426368.png
    deleted file mode 100644
    index 3aa6c00..0000000
    --- a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612190426368.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:b8a608027cadcfd5e81468e7e52fa38430deed803e52c06fec8168ccb5bb3db5
    -size 127594
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612190426368.webp b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612190426368.webp
    new file mode 100644
    index 0000000..5c171ad
    --- /dev/null
    +++ b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612190426368.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:6b3ecec450c6c7d39fc262f2cca842f776b00a5a17fd90592fbf5d5bac9ef95b
    +size 102504
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612192700169.png b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612192700169.png
    deleted file mode 100644
    index dc9e6e7..0000000
    --- a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612192700169.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:0567f8196b2476e6fdcfd72c27bb1cb6cc2b444ff0f3264746d1db37eddc01af
    -size 46866
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612192700169.webp b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612192700169.webp
    new file mode 100644
    index 0000000..abf867a
    --- /dev/null
    +++ b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612192700169.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:291b80deb01bef18bebbf3e7774bd0ea9b65f8a6ba54b6d0609c7e98a45a1f45
    +size 21358
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612193301372.png b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612193301372.png
    deleted file mode 100644
    index d49b50b..0000000
    --- a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612193301372.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:d1ddd98fa16315aff0d31e4989879c730bad698d82920068592ca2d7788cb296
    -size 127169
    diff --git a/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612193301372.webp b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612193301372.webp
    new file mode 100644
    index 0000000..a276da0
    --- /dev/null
    +++ b/YaeBlog/source/posts/computer-architecture-pipeline/image-20240612193301372.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:4c57f6d1869747aed4f08714f14e3c4c5ac88c3d601e2f7e90d2c75fc7dd998b
    +size 86268
    diff --git a/YaeBlog/source/posts/daily-linux-0.md b/YaeBlog/source/posts/daily-linux-0.md
    index 98f1486..1b50da4 100644
    --- a/YaeBlog/source/posts/daily-linux-0.md
    +++ b/YaeBlog/source/posts/daily-linux-0.md
    @@ -1,12 +1,12 @@
     ---
     title: 日用Linux挑战 第0篇 初见Arch Linux
    +date: 2023-01-15T22:23:08.0000000
     tags:
    -  - Linux
    -  - 杂谈
    -date: 2023-01-15 22:23:08
    -typora-root-url: daily-linux-0
    +- Linux
    +- 杂谈
     ---
     
    +
     在将开发重心移到`WSL`上一年之后,我最终还是决定完全抛弃Windows,转向使用Linux作为我日常使用的主力系统。目前,我已经使用Linux作为主力系统一个月了。
     
     
    @@ -92,7 +92,7 @@ sudo systemctl enable sddm.service
     
     我目前实现的效果大概长这样:
     
    -![](2023-01-12-13-28-38-Screenshot_20230112_132829.png)
    +![](2023-01-12-13-28-38-Screenshot_20230112_132829.webp)
     
     颇有一种`Windows`和`MacOS`杂交的风格。
     
    @@ -106,7 +106,7 @@ sudo systemctl enable sddm.service
     
     先上一张`shell`的系统概览截图:
     
    -![](2023-01-12-13-36-45-Screenshot_20230112_133628.png)
    +![](2023-01-12-13-36-45-Screenshot_20230112_133628.webp)
     
     终端模拟器直接使用的`konsole`,目前没有进行改动。
     
    diff --git a/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-28-38-Screenshot_20230112_132829.png b/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-28-38-Screenshot_20230112_132829.png
    deleted file mode 100644
    index 98b6c8e..0000000
    --- a/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-28-38-Screenshot_20230112_132829.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:92d829e0fd67280961dc68ed96477c788edfcf639186844ca91f35ed4ac10e74
    -size 2874354
    diff --git a/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-28-38-Screenshot_20230112_132829.webp b/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-28-38-Screenshot_20230112_132829.webp
    new file mode 100644
    index 0000000..219d603
    --- /dev/null
    +++ b/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-28-38-Screenshot_20230112_132829.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:6482d2eddccfe45c38a1fa3aa22ab7b92cba0fae89a5f7f957b039b363802e54
    +size 141138
    diff --git a/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-36-45-Screenshot_20230112_133628.png b/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-36-45-Screenshot_20230112_133628.png
    deleted file mode 100644
    index 6a74eeb..0000000
    --- a/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-36-45-Screenshot_20230112_133628.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:e57adb3e2bab147ce042cc51d72483ddb9842e3eda85442b0ad988d660a7012b
    -size 227688
    diff --git a/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-36-45-Screenshot_20230112_133628.webp b/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-36-45-Screenshot_20230112_133628.webp
    new file mode 100644
    index 0000000..e991e55
    --- /dev/null
    +++ b/YaeBlog/source/posts/daily-linux-0/2023-01-12-13-36-45-Screenshot_20230112_133628.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:1a073ba53b1db67218108de9094841424e42dd29fe17762ebcf49b01e6dc3b9e
    +size 75008
    diff --git a/YaeBlog/source/posts/daily-linux-2.md b/YaeBlog/source/posts/daily-linux-2.md
    index 3960298..501276f 100644
    --- a/YaeBlog/source/posts/daily-linux-2.md
    +++ b/YaeBlog/source/posts/daily-linux-2.md
    @@ -1,13 +1,13 @@
     ---
     title: 日用Linux挑战 第2篇 Wayland
    +date: 2023-07-23T11:44:34.0000000
     tags:
    -  - 杂谈
    -  - Linux
    -date: 2023-07-23 11:44:34
    -typora-root-url: daily-linux-2
    +- 杂谈
    +- Linux
     ---
     
     
    +
     使用`Linux`6个月,我成功戒掉了原神。
     
     
    @@ -18,7 +18,7 @@ typora-root-url: daily-linux-2
     
     最近恰好被平铺式的窗口管理器种草,又在B站上看见一个动画绚丽的`wayland`合成器——[Hyprland](https://hyprland.org/),当即脑袋一热,就把`kde`干掉,装上了`hyprland`。
     
    -![img](df4211f6be2724b3b4725f7ce5a4078818844857.jpg)
    +![img](df4211f6be2724b3b4725f7ce5a4078818844857.avif)
     
     安装`hyprland`的过程非常舒适,`hyprland`被打包为一个单独的二进制文件,使用`pacman`安装之后直接在`tty`下执行:
     
    @@ -46,7 +46,7 @@ Hyprland
     
     各种在学习过程中遇到的工具软件:基本上都工作运行良好。当然因为没有设置缩放的问题而导致字体都很小。因为如果在配置文件中设置缩放之后会导致字体发虚。下面的截图就是我将我的2K显示屏设置为150%缩放的效果,~~虽然在截图中的效果不明显~~。目前在常用软件中唯一让我十分不满意的软件是`wps`,使用体验完全无法和`offices`相提并论,目前我正在研究使用`wine`运行`offices`,如果成功了就再水一篇博客庆祝一下。
     
    -![image-20230702205919301](image-20230702205919301.png)
    +![image-20230702205919301](image-20230702205919301.webp)
     
     > 最新的进展是使用`wine`没法安装学校提供的`office 2021`,同时我又不愿意使用古老的`office`版本,但是我发现一个称作`onlyoffice`的第三方软件蛮好用的,等我试用一段时间再说。
     >
    diff --git a/YaeBlog/source/posts/daily-linux-2/df4211f6be2724b3b4725f7ce5a4078818844857.jpg b/YaeBlog/source/posts/daily-linux-2/df4211f6be2724b3b4725f7ce5a4078818844857.avif
    similarity index 100%
    rename from YaeBlog/source/posts/daily-linux-2/df4211f6be2724b3b4725f7ce5a4078818844857.jpg
    rename to YaeBlog/source/posts/daily-linux-2/df4211f6be2724b3b4725f7ce5a4078818844857.avif
    diff --git a/YaeBlog/source/posts/daily-linux-2/image-20230702205919301.png b/YaeBlog/source/posts/daily-linux-2/image-20230702205919301.png
    deleted file mode 100644
    index 0344fa1..0000000
    --- a/YaeBlog/source/posts/daily-linux-2/image-20230702205919301.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:48a0c7ae799eca73de2ed8ed889b7ca6efd4891174ea746ee12bb97c4a2d444f
    -size 464377
    diff --git a/YaeBlog/source/posts/daily-linux-2/image-20230702205919301.webp b/YaeBlog/source/posts/daily-linux-2/image-20230702205919301.webp
    new file mode 100644
    index 0000000..4426c73
    --- /dev/null
    +++ b/YaeBlog/source/posts/daily-linux-2/image-20230702205919301.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:ec9b887868bff77ddd78f214b9dbd57a043e0cfa72ff6d9016df6b98a0620d82
    +size 54620
    diff --git a/YaeBlog/source/posts/daily-linux-3.md b/YaeBlog/source/posts/daily-linux-3.md
    index ae507a4..3d8117c 100644
    --- a/YaeBlog/source/posts/daily-linux-3.md
    +++ b/YaeBlog/source/posts/daily-linux-3.md
    @@ -1,13 +1,13 @@
     ---
     title: 日用Linux挑战 第3篇 放弃Wayland
    +date: 2023-09-04T14:47:46.0000000
     tags:
    -  - 杂谈
    -  - Linux
    -typora-root-url: daily-linux-3
    -date: 2023-09-04 14:47:46
    +- 杂谈
    +- Linux
     ---
     
     
    +
     成也开源,败也开源。
     
     
    @@ -53,7 +53,7 @@ date: 2023-09-04 14:47:46
       - `Meta+F`全屏应用
       - `Meta+W`关闭应用
     
    -![](Screenshot_20230904_144149.png)
    +![](Screenshot_20230904_144149.webp)
     
     ### Fuck You NVIDIA
     
    diff --git a/YaeBlog/source/posts/daily-linux-3/Screenshot_20230904_144149.png b/YaeBlog/source/posts/daily-linux-3/Screenshot_20230904_144149.png
    deleted file mode 100644
    index 3798ab2..0000000
    --- a/YaeBlog/source/posts/daily-linux-3/Screenshot_20230904_144149.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:0cd5c1c81e8faa1acdfeb3a3fbf65116970ee79fb811c08b23fcf91b1570d64d
    -size 3049270
    diff --git a/YaeBlog/source/posts/daily-linux-3/Screenshot_20230904_144149.webp b/YaeBlog/source/posts/daily-linux-3/Screenshot_20230904_144149.webp
    new file mode 100644
    index 0000000..eb11f2f
    --- /dev/null
    +++ b/YaeBlog/source/posts/daily-linux-3/Screenshot_20230904_144149.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:f9c45c7a0eb055513f7bb2804793cac662033d6d3a49e0244b45df730145df0b
    +size 158686
    diff --git a/YaeBlog/source/posts/daily-linux-4.md b/YaeBlog/source/posts/daily-linux-4.md
    index 82b364d..ec61f7d 100644
    --- a/YaeBlog/source/posts/daily-linux-4.md
    +++ b/YaeBlog/source/posts/daily-linux-4.md
    @@ -1,11 +1,12 @@
     ---
     title: 日用Linux挑战 第4篇 新的开始
    +date: 2024-03-09T14:00:00.0000000
     tags:
    -  - Linux
    -  - 杂谈
    -date: 2024/03/09 14:00:00
    +- Linux
    +- 杂谈
     ---
     
    +
     小步快跑,面向未来。
     
     
    @@ -18,7 +19,7 @@ date: 2024/03/09 14:00:00
     
     相较于古老但是稳定的`Ext4`文件系统,`Btrfs`对我来说最大的好处便是可以零成本的创建快照,便于在出现错误的时候及时回滚或者直接重装系统。因此,为了方便快照的生成和回滚,我在安装系统时使用**扁平化**的子分区划分方法:即尽力避免出现嵌套的子分区,所有需要快照的分区都处在`/`目录之下:
     
    -![Screenshot_20240309_115143](daily-linux-4/Screenshot_20240309_115143.png)
    +![Screenshot_20240309_115143](daily-linux-4/Screenshot_20240309_115143.webp)
     
     - `@`为根分区,挂载在`/`目录之下,打开写时复制;
     - `@home`为家目录分区,挂载在`/home`目录之下,打开写时复制;
    @@ -44,7 +45,7 @@ sudo btrfs send /.snapshots/home@20240225 | zstd | ssh root@remote "zstd -d | bt
     
     算起来,我已经和`Wayland`显示协议相爱相杀了整整一年了,从`KDE plasma X`到`Hyprland`,再尝试小众的`labwc`,最后回到了`KDE plasma X`。而在2024年2月29日`KDE plasma`释出6.0版本,将`Wayland`作为默认的显示协议,我也在第一时间更新了版本并使用`wayland`显示协议。现在,我可以比较确定的说,`Wayland`目前已经达到可用的水平了,而且我还是使用`RTX 3060`显卡。
     
    -![image-20240309130329784](daily-linux-4/image-20240309130329784.png)
    +![image-20240309130329784](daily-linux-4/image-20240309130329784.webp)
     
     不过相较于`AMDGPU`可以开箱即用,使用`NVIDIA`启动需要配置如下的模块参数:
     
    @@ -64,7 +65,7 @@ options nvidia_drm modeset=1 fbdev=1
     
     不过`XWayland`应用程序在使用`NVIDIA`驱动时会存在一个神奇的**同步失败**问题,表现为在`xwayland`中部分控件闪烁,交替显示更新前和更新后的帧,而且这个问题几乎不能被截屏抓到,具体可以见`freedesktop`上的这个[issue](https://gitlab.freedesktop.org/xorg/xserver/-/issues/1317)。虽然这个议题下面有着很长的讨论,还是建议大家完整的看一遍,里面甚至还有:
     
    -![image-20240309131750535](daily-linux-4/image-20240309131750535.png)
    +![image-20240309131750535](daily-linux-4/image-20240309131750535.webp)
     
     省流:这个议题讨论了在`xserver`中提供显式同步的协议原语,方便图形驱动程序知道什么时候渲染的帧发生了变化。因此这并不是一个`NVIDIA`驱动程序的问题,而是需要将`Linux`显示协议栈从隐式同步迁移到显式同步。但是相关的工作还在开发过程中,因此解决方法有两个:
     
    @@ -77,9 +78,9 @@ options nvidia_drm modeset=1 fbdev=1
     
     于是,我就在`Arch Wiki`上学到一条新知识:
     
    -![image-20240309134847166](daily-linux-4/image-20240309134847166.png)
    +![image-20240309134847166](daily-linux-4/image-20240309134847166.webp)
     
     原来`efi`分区其实只用放`grub`,,,
     
    -![img](daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.jpg)
    +![img](daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.webp)
     
    diff --git a/YaeBlog/source/posts/daily-linux-4/Screenshot_20240309_115143.png b/YaeBlog/source/posts/daily-linux-4/Screenshot_20240309_115143.png
    deleted file mode 100644
    index f2d5911..0000000
    --- a/YaeBlog/source/posts/daily-linux-4/Screenshot_20240309_115143.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:56cb495f0a6f57c1b72eef008faa2b4ef9f572c2c2c2a8c6962cf6df5787fae7
    -size 79552
    diff --git a/YaeBlog/source/posts/daily-linux-4/Screenshot_20240309_115143.webp b/YaeBlog/source/posts/daily-linux-4/Screenshot_20240309_115143.webp
    new file mode 100644
    index 0000000..b4250bd
    --- /dev/null
    +++ b/YaeBlog/source/posts/daily-linux-4/Screenshot_20240309_115143.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:61a07177866e25ab9b2b7b3d1eb260e1fbac17c9b1be428966d1a9fece5efe04
    +size 13498
    diff --git a/YaeBlog/source/posts/daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.jpg b/YaeBlog/source/posts/daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.jpg
    deleted file mode 100644
    index 0217928..0000000
    --- a/YaeBlog/source/posts/daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.jpg
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:9195301a60278833cb62f6971a1cee5ac0a2aa0bc31770b79fe3c08872ac1ae6
    -size 574570
    diff --git a/YaeBlog/source/posts/daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.webp b/YaeBlog/source/posts/daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.webp
    new file mode 100644
    index 0000000..d55647c
    --- /dev/null
    +++ b/YaeBlog/source/posts/daily-linux-4/cfd17cff0701a8e8c69fecf247f17fc1-1709963611271-2.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:4eae5bfda7882b8389e9a4dc1eae23468bb4d42270338913f0bd292245207f07
    +size 201904
    diff --git a/YaeBlog/source/posts/daily-linux-4/image-20240309130329784.png b/YaeBlog/source/posts/daily-linux-4/image-20240309130329784.png
    deleted file mode 100644
    index c2f9d16..0000000
    --- a/YaeBlog/source/posts/daily-linux-4/image-20240309130329784.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:381b47a8cb243352ad1a9f57fab45f49cc1b8bd450aeeab4ed1a699359953669
    -size 395168
    diff --git a/YaeBlog/source/posts/daily-linux-4/image-20240309130329784.webp b/YaeBlog/source/posts/daily-linux-4/image-20240309130329784.webp
    new file mode 100644
    index 0000000..255d4de
    --- /dev/null
    +++ b/YaeBlog/source/posts/daily-linux-4/image-20240309130329784.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:1b77fb0a670449e075dbedb15f48ca15a3af970ec78aa2044260b08cebb45af3
    +size 106782
    diff --git a/YaeBlog/source/posts/daily-linux-4/image-20240309131750535.png b/YaeBlog/source/posts/daily-linux-4/image-20240309131750535.png
    deleted file mode 100644
    index 85658f3..0000000
    --- a/YaeBlog/source/posts/daily-linux-4/image-20240309131750535.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:938adf2b3037b870c6291878d9470a2b275f642e8a9fff739b23fada33a27f97
    -size 157597
    diff --git a/YaeBlog/source/posts/daily-linux-4/image-20240309131750535.webp b/YaeBlog/source/posts/daily-linux-4/image-20240309131750535.webp
    new file mode 100644
    index 0000000..a88aef4
    --- /dev/null
    +++ b/YaeBlog/source/posts/daily-linux-4/image-20240309131750535.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:66d2533db6f4f7ff214c3d8fa35da1a427609caa5cf72a1594d0ca3aab7db2f5
    +size 61926
    diff --git a/YaeBlog/source/posts/daily-linux-4/image-20240309134847166.png b/YaeBlog/source/posts/daily-linux-4/image-20240309134847166.png
    deleted file mode 100644
    index e61c30a..0000000
    --- a/YaeBlog/source/posts/daily-linux-4/image-20240309134847166.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:2e76a2dd3c1fa0382410c0e980d42e0c27ed3a0e198036b4b5d65ee69122266c
    -size 38275
    diff --git a/YaeBlog/source/posts/daily-linux-4/image-20240309134847166.webp b/YaeBlog/source/posts/daily-linux-4/image-20240309134847166.webp
    new file mode 100644
    index 0000000..32b174d
    --- /dev/null
    +++ b/YaeBlog/source/posts/daily-linux-4/image-20240309134847166.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:d02f73b1307040111112191abc11959e0725c979dc0e0768233a8c9df63896b1
    +size 20340
    diff --git a/YaeBlog/source/posts/dotnet-performance-8.md b/YaeBlog/source/posts/dotnet-performance-8.md
    index e2af79d..2657e90 100644
    --- a/YaeBlog/source/posts/dotnet-performance-8.md
    +++ b/YaeBlog/source/posts/dotnet-performance-8.md
    @@ -7,6 +7,7 @@ tags:
     - 编译原理
     ---
     
    +
     JIT编译就一定比AOT编译慢吗?
     
     
    @@ -104,7 +105,7 @@ class Program
     
     首先是为了更好发挥动态PGO的性能,JIT编译器中为分层编译引入了更多的编译层数。需要引入更多编译层数的原因主要有两点。第一,插入各种采样的指令和代码是需要代价的,考虑到第0层编译的主要目标是为了降低编译的时间,提高应用的启动速度,在第0层编译过程中就不能插入太多的采样指令。因此编译器首先增加了一个新的编译层——采样第0层来解决这个问题。大部分的方法将在第一次运行时编译到缺少优化、缺少采样指令的第0层,在运行时发现该方法被调用了多次之后,JIT编译器将这个方法重新编译到采样第0层,再经过一系列的调用之后,JIT编译器将利用采样得到的信息对该方法重新进行编译并优化。第二,在原始编译器模型中使用即时运行(R2R)方法编译的代码不能参加到动态PGO中,尤其是考虑到几乎所有应用程序都会调用的核心库代码是采用R2R的方式进行运行的,如果这部分的代码不能参加动态PGO将不能够完全发挥动态PGO的效果,虽然核心库在提前编译的过程中会使用静态PGO进行一部分的优化。因此JIT编译器为R2R编译好的代码增加了一个新的编译器,在运行时发现这部分代码被调用多次之后将会被JIT编译器编译到含有优化和采样代码的采样第1层,随着调用次数的增加这部分的代码将可以利用采样得到的信息进行优化。下面这张图展现了不同编译方法在运行过程中可能达到的编译层级。
     
    -![image-20240828135354598](./dotnet-performance-8/image-20240828135354598.png)
    +![image-20240828135354598](./dotnet-performance-8/image-20240828135354598.webp)
     
     JIT编译器也在第0层编译的过程中引入了更多的优化。虽然第0层编译的目的是缩短编译的时间,但是许多的优化可以通过减少需要生成的代码数量来达到这个目的。常量折叠(Constant Folding)就是一个很好的例子。虽然这会让JIT编译器在第0层编译时花费更多的时间同运行时中的虚拟机交互来解析各种变量的类型,但是这可以大量的减少JIT编译器需要生成的代码量,尤其是对于下面这种涉及到类型判断的例子。
     
    @@ -257,7 +258,7 @@ for (int trial = 0; trial < 5; trial++)
     
     程序的输出是5次次采样统计的结果:
     
    -![image-20240828155556375](./dotnet-performance-8/image-20240828155556375.png)
    +![image-20240828155556375](./dotnet-performance-8/image-20240828155556375.webp)
     
     需要指出的是,虽然在上面的代码中使用和运行时代码中一样的“蓄水池”大小,但是在运行时并没有提前获得所有需要统计的数据,调用的统计数据是由多个不同的运行线程同时写入蓄水池中的。从结果中可以看出,虽然数值上并不准确,但是该算法准确的统计出了各个字符的出现趋势。
     
    @@ -532,7 +533,7 @@ public class Tests
     
     为了优化这种生成周期和程序一致对象的内存管理,.NET 8中引入了一个新的堆——没有内存管理的堆。JIT编译器将会保证这些常量类型的对象将会被分配在这个堆中,这种没有GC管理的堆也意味着JIT编译器可以为这些对象使用一个固定的内存地址,在使用时避免掉了一次内存读取。
     
    -![Heaps where .NET Objects Live](./dotnet-performance-8/HeapsWhereNetObjectsLive.png)
    +![Heaps where .NET Objects Live](./dotnet-performance-8/HeapsWhereNetObjectsLive.webp)
     
     将上述提高的示例代码使用.NET 8版本进行编译得到的代码如下,从中也可以看出JIT编译器生成的代码只有一条`mov`指令,避免了一次内存访问。
     
    diff --git a/YaeBlog/source/posts/dotnet-performance-8/HeapsWhereNetObjectsLive.png b/YaeBlog/source/posts/dotnet-performance-8/HeapsWhereNetObjectsLive.png
    deleted file mode 100644
    index f313abe..0000000
    --- a/YaeBlog/source/posts/dotnet-performance-8/HeapsWhereNetObjectsLive.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:51d6acd5b3bb8134b3a56602328f948e391679492753934d7514a2ff10851d25
    -size 21885
    diff --git a/YaeBlog/source/posts/dotnet-performance-8/HeapsWhereNetObjectsLive.webp b/YaeBlog/source/posts/dotnet-performance-8/HeapsWhereNetObjectsLive.webp
    new file mode 100644
    index 0000000..87f2d5e
    --- /dev/null
    +++ b/YaeBlog/source/posts/dotnet-performance-8/HeapsWhereNetObjectsLive.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:8c06db81eb612adf2c9110087a1f4ff121ac5fdb1bfaabb1aad22615e63a8505
    +size 19238
    diff --git a/YaeBlog/source/posts/dotnet-performance-8/image-20240828135354598.png b/YaeBlog/source/posts/dotnet-performance-8/image-20240828135354598.png
    deleted file mode 100644
    index aa123bb..0000000
    --- a/YaeBlog/source/posts/dotnet-performance-8/image-20240828135354598.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:6274b29f3b6dd75841ad0c36f2595403f9f326f2c90ed5ef062a3366a4f3fd9c
    -size 62003
    diff --git a/YaeBlog/source/posts/dotnet-performance-8/image-20240828135354598.webp b/YaeBlog/source/posts/dotnet-performance-8/image-20240828135354598.webp
    new file mode 100644
    index 0000000..24c61ad
    --- /dev/null
    +++ b/YaeBlog/source/posts/dotnet-performance-8/image-20240828135354598.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:3e250fddca641dd8afcd3065cf90d74bb2f69d5ea15797dfdbde12b2cd7b506a
    +size 17798
    diff --git a/YaeBlog/source/posts/dotnet-performance-8/image-20240828155556375.png b/YaeBlog/source/posts/dotnet-performance-8/image-20240828155556375.png
    deleted file mode 100644
    index 763322f..0000000
    --- a/YaeBlog/source/posts/dotnet-performance-8/image-20240828155556375.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:f38ca49381035d50cea7549223f5b2b2f4ea27eebb286920b67b6f3784a8982b
    -size 38001
    diff --git a/YaeBlog/source/posts/dotnet-performance-8/image-20240828155556375.webp b/YaeBlog/source/posts/dotnet-performance-8/image-20240828155556375.webp
    new file mode 100644
    index 0000000..ba912fe
    --- /dev/null
    +++ b/YaeBlog/source/posts/dotnet-performance-8/image-20240828155556375.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:d39a580333d9f7d214f9cc9c38d4b2b70bbec4267edfddb342f0e711c409888d
    +size 9904
    diff --git a/YaeBlog/source/posts/environment-setting.md b/YaeBlog/source/posts/environment-setting.md
    index 704c465..5130383 100644
    --- a/YaeBlog/source/posts/environment-setting.md
    +++ b/YaeBlog/source/posts/environment-setting.md
    @@ -1,11 +1,11 @@
     ---
     title: 环境配置备忘录
    -date: 2022-01-15 20:19:39
    +date: 2022-01-15T20:19:39.0000000
     tags:
    -    - 技术笔记
    -typora-root-url: 环境配置
    +- 技术笔记
     ---
     
    +
     电脑上的环境三天两头出问题,写下一个备忘录记录一下电脑上环境的配置过程。
     
     
    @@ -236,7 +236,7 @@ Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope CurrentUser
     
     再重新运行配置文件就没有问题了。
     
    -![终端预览](6.png)
    +![终端预览](6.webp)
     
     ### PowerShell配置文件
     
    diff --git a/YaeBlog/source/posts/environment-setting/6.png b/YaeBlog/source/posts/environment-setting/6.png
    deleted file mode 100644
    index b422cff..0000000
    --- a/YaeBlog/source/posts/environment-setting/6.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:d4611461d3ae3af16c71f30bed786cd52928889d13e94cbd6e289676c201a352
    -size 2794913
    diff --git a/YaeBlog/source/posts/environment-setting/6.webp b/YaeBlog/source/posts/environment-setting/6.webp
    new file mode 100644
    index 0000000..d6f95ee
    --- /dev/null
    +++ b/YaeBlog/source/posts/environment-setting/6.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:c46691ac920565f3dfc282600f84a39af616e04d87c3b1ba8a2e2aa2b77b9b4d
    +size 76532
    diff --git a/YaeBlog/source/posts/genshin-gacha-1.md b/YaeBlog/source/posts/genshin-gacha-1.md
    index 5b033de..ea8c77f 100644
    --- a/YaeBlog/source/posts/genshin-gacha-1.md
    +++ b/YaeBlog/source/posts/genshin-gacha-1.md
    @@ -1,11 +1,12 @@
     ---
     title: 原神抽卡研究一
    +date: 2022-12-31T13:38:19.0000000
     tags:
    -  - 原神
    -  - 学习资料
    -date: 2022-12-31 13:38:19
    +- 原神
    +- 学习资料
     ---
     
    +
     实际上是“概率论和随机过程”课程的期末小论文。
     
     
    @@ -14,7 +15,7 @@ date: 2022-12-31 13:38:19
     
     目前在市面上出现了大量以抽奖为核心盈利手段的电子游戏,在这种游戏中,获取游戏中的物品不是明码标价的购买,而是通过参加某种抽奖性质的活动。玩家花费一定金额购买参加活动的机会,每次参加都有一定的概率获得玩家想要获得的物品。在抽奖活动中,游戏设计者还会引入一种被称为 “保底” 的游戏机制:开发者向玩家群体承诺在一定的参加次数之后必然会获得到该物品。例如下面是热门游戏《原神》中获取游戏中角色 “抽奖” 活动的概率公示页面。
     
    -![](./genshin-gacha-1/2022-12-31-13-06-36-image.png)
    +![](./genshin-gacha-1/2022-12-31-13-06-36-image.webp)
     
     在这个 “抽奖” 活动中,玩家可以得到三种等级的物品,在游戏中分别称为 “五星物品”、“四星物品” 和 “三星物品”。在每次的 “抽奖” 活动中,玩家必定会获得上述三种物品中的一种。为了简化问题的讨论,我们现不区分相同等级不同物品之间的不同,只考虑不同星级物品的获取概率。通过概率公示可以知道:五星物品的 “基础概率” 为0.600%,四星物品的 “基础概率” 为 5.100%;五星物品的 “综合概率” 为 1.600%,四星物品的 “综合概率” 为 13.000%。从一个玩家的角度出发,自然会存在两个问题:
     
    @@ -50,11 +51,11 @@ date: 2022-12-31 13:38:19
     
     在总共 4842256 次抽卡记录中,获得五星物品的次数为 78493 次,于是:
     
    -![](genshin-gacha-1/2022-12-31-15-59-20-image.png)
    +![](genshin-gacha-1/2022-12-31-15-59-20-image.webp)
     
     再计算一下每次参加该活动获得四星物品的平均概率:在总共 5000139 次抽卡中,获得四星物品的次数为 653200:
     
    -![](genshin-gacha-1/2022-12-31-15-59-42-image.png)
    +![](genshin-gacha-1/2022-12-31-15-59-42-image.webp)
     
     不难发现,在误差允许的范围内,计算出来的平均概率和游戏开发者所公布的 “综合概率” 是相同的。当参与这个游戏足够多次时,获取到五星物品和四星物品的数量就可以用这个概率来估计。
     
    @@ -62,15 +63,15 @@ date: 2022-12-31 13:38:19
     
     为了方便讨论,再次将这个 “游戏” 简化为获得五星物品和不获得五星物品两种情况。那么这个 “抽奖” 游戏是否就能被简化为一个概率为 1.6% 的 n 次伯努利实验?不妨假设每次获得五星物品之间相互独立,这样每次获得五星物品都可以认为是首次获得五星物品,这时参加该游戏的次数就会符合概率为 1.6% 的几何分布,而为了符合保底规则,当玩家在参与到第 90 次时仍未获得五星物品,强制给予玩家一个五星的物品。画出实际数据中得到的图像和按照几何分布得到的图像。
     
    -![](genshin-gacha-1/2022-12-31-13-20-46-image.png)
    +![](genshin-gacha-1/2022-12-31-13-20-46-image.webp)
     
    -![](genshin-gacha-1/2022-12-31-13-21-11-image.png)
    +![](genshin-gacha-1/2022-12-31-13-21-11-image.webp)
     
     不难发现假设的猜想和实际情况不相符合。在抽数小于 73 抽时,获得五星物品的概率逐渐降低,从 0.6% 左右一直降低至 0.4% 左右。当抽数大于等于 73 抽时,抽到的概率开始上升,在抽数等于 77 抽时达到最大,大约为 10.4%。随后概率开始下降,在第 91 抽时,概率等于 0。
     
     如果在实际得到的概率关于抽数的图上再作出概率为 0.6% 的几何分布的图像,前73 抽的概率图像和几何分布的图像几乎吻合。也就是说,该抽奖游戏的前 73 抽就是一个符合 P = 0.6% 的几何分布,从第 73 抽开始 “保底” 机制的修正。这就是游戏开发者口中 “基础概率” 的含义:在该抽奖游戏的前数十抽就是一个概率为 0.6% 的伯努利实验。
     
    -![](genshin-gacha-1/2022-12-31-13-24-26-image.png)
    +![](genshin-gacha-1/2022-12-31-13-24-26-image.webp)
     
     从大量的实际数据出发,不难发现游戏开发者的申明同实际情况相吻合。
     
    @@ -80,7 +81,7 @@ date: 2022-12-31 13:38:19
     
     直到作者开始写作本文之前,作者都没有意识到本文 3.2 节中的所有结论几乎都基于该假设。限于文章的篇幅原因和个人的能力问题,在本文中未对这个假设作出验证。下面给出一种验证该猜想的方法。为验证获得五星物品之间相互独立,可以通过统计方法验证下面的等式成立。
     
    -![](genshin-gacha-1/2022-12-31-16-00-10-image.png)
    +![](genshin-gacha-1/2022-12-31-16-00-10-image.webp)
     
     ### 展望
     
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-06-36-image.png b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-06-36-image.png
    deleted file mode 100644
    index 0903b38..0000000
    --- a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-06-36-image.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:2ed01520f4d7e6bde13d6afbe12e8739506bde28d4ae8658c757296e8f435c29
    -size 111575
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-06-36-image.webp b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-06-36-image.webp
    new file mode 100644
    index 0000000..dedf95f
    --- /dev/null
    +++ b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-06-36-image.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:7e66d8f534067205e66b62036ce7b1e79e82bf3ac2e87f85cc3c45eb54620ca9
    +size 61390
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-20-46-image.png b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-20-46-image.png
    deleted file mode 100644
    index bd4c2f9..0000000
    --- a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-20-46-image.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:099b6ce419578d9016c7d9644bb6aef0fe3ad73f4402a29c3bf6feaca2914b3a
    -size 46251
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-20-46-image.webp b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-20-46-image.webp
    new file mode 100644
    index 0000000..0b74140
    --- /dev/null
    +++ b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-20-46-image.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:827e460e1fb07ad40f7225c26e887b5dd7c107b18651a8150693a3a221c8544b
    +size 22780
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-21-11-image.png b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-21-11-image.png
    deleted file mode 100644
    index 463aac2..0000000
    --- a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-21-11-image.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:c8ec4f1a2c6c0184c86d8cce52e51e06f376f228c14307eac1e41c4ed6e58ed6
    -size 37762
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-21-11-image.webp b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-21-11-image.webp
    new file mode 100644
    index 0000000..2bc2a1d
    --- /dev/null
    +++ b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-21-11-image.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:6f484ca74739d3ae8724a7905d0c3ea353ad4a83e6a8ffb92dced9d37fab3c03
    +size 19342
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-24-26-image.png b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-24-26-image.png
    deleted file mode 100644
    index 6ccee36..0000000
    --- a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-24-26-image.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:b4aa0365c268a0104d81cc4f0273254d3568f7dd48b05aefc51f7496f81c8512
    -size 46392
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-24-26-image.webp b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-24-26-image.webp
    new file mode 100644
    index 0000000..bdd8dc6
    --- /dev/null
    +++ b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-13-24-26-image.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:78e2153061ee743bfdf651d33fd8c1587238aa4a5f827c9c8e471fae278e79ad
    +size 26180
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-20-image.png b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-20-image.png
    deleted file mode 100644
    index b6ddba3..0000000
    --- a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-20-image.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:662bfafbae8857dc9a1349c6bf51b8b122a93ebd181ed50e0e4413204f1dc0d0
    -size 10280
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-20-image.webp b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-20-image.webp
    new file mode 100644
    index 0000000..879b8ba
    --- /dev/null
    +++ b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-20-image.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:b416dcbb9b458e9e1594de9aac004145e33af82a679f802301659be7af19564a
    +size 7328
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-42-image.png b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-42-image.png
    deleted file mode 100644
    index e74abec..0000000
    --- a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-42-image.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:b9cae7637db04d556cded06019bab4454284b6a2c122382c865573ccd08df175
    -size 10578
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-42-image.webp b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-42-image.webp
    new file mode 100644
    index 0000000..b45503c
    --- /dev/null
    +++ b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-15-59-42-image.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:0c1a3c34ee5b7309dc2c638e540f4e933d6354aeb4b47d9f7099bfae41768df7
    +size 6974
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-16-00-10-image.png b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-16-00-10-image.png
    deleted file mode 100644
    index c5146bf..0000000
    --- a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-16-00-10-image.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:5fe30a67495ed6c97cafd4dcfd4432709f871bb86738596ca6a738ea5b4744fe
    -size 18066
    diff --git a/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-16-00-10-image.webp b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-16-00-10-image.webp
    new file mode 100644
    index 0000000..3547a77
    --- /dev/null
    +++ b/YaeBlog/source/posts/genshin-gacha-1/2022-12-31-16-00-10-image.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:c3b9987a736145d91614f59dac655fc9eacd390cabfb2c4cdc684c6f1cce36af
    +size 10434
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model.md b/YaeBlog/source/posts/heterogeneous-programming-model.md
    index 7e5a702..5b9511a 100644
    --- a/YaeBlog/source/posts/heterogeneous-programming-model.md
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model.md
    @@ -7,6 +7,7 @@ tags:
     ---
     
     
    +
     随着摩尔定律的逐渐失效,将CPU和其他架构的计算设备集成在片上或者通过高速总线互联构建的异构系统成为了高性能计算的主流。但是在这种系统中,上层应用的设计与实现面临着异构系统中各个设备之间体系结构差异过大、缺乏良好的异构抽象以及统一的编程接口和应用程序的优化难度大等困难。
     
     异构并行编程模型便是解决这些编程和执行效率问题的解决方案。
    @@ -25,11 +26,11 @@ tags:
     
     首先是异构系统中各个设备之间的并行计算能力不同。在同构的并行计算系统中,比如多核CPU中,虽然同一CPU的不同核之间、同一核的不同SIMD部件之间可以承担不同粒度的并行计算任务,但是其并行计算的能力是完全相同的。但是在一个典型的异构计算系统,例如CPU、GPU和FPGA组成的异构系统,不同设备的微架构具有本质差异,其并行计算的模式和能力都完全不同,设备之间的特长也完全不同。这种设备之间并行计算能力的差异使得系统中的任务划分和任务映射不再是均一的,而是具有显著的特异性。这种特点虽然也有利于表达实际应用的特点,但是却给异构并行计算模型的设计带来了巨大的困难。
     
    -![9eb06d8be92ddef3db33e040163c67a7.png](./heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.png)
    +![9eb06d8be92ddef3db33e040163c67a7.webp](./heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.webp)
     
     其次是异构系统中加速设备数据分布可配置、设备间数据通信渠道多样性给数据分布和通信带来的困难。在同构并行系统中,CPU片内的存储是对于软件透明的缓存架构,在片外则是一个共享内存模型,因此在这类系统中,数据仅可能分布在片外的共享存储中,具有存储位置单一的特点,也不需要进行显式的通信操作。但是在异构系统中,不仅在单个加速设备内部可能有软件可分配的快速局部存储,设备之间的连接方式差异也很大。目前,大多个加速设备都是通过PCIe总线的方式同CPU进行连接,这使得加速设备无法通过和CPU相同的方式完成地址映射,存在某一设备无法访问另一设备片外存储的问题。这使得异构系统中数据可以分布在CPU、加速设备的片外存储和加速设备的片内多层次局部存储等多个位置,不仅使得编程模型的数据分布问题变得十分复杂,设备间的通信文件也可能需要显式进行。
     
    -![eab553f9e30d8d866a1ddd201b5e4c85.png](./heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.png)
    +![eab553f9e30d8d866a1ddd201b5e4c85.webp](./heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.webp)
     
     最后是异构系统中多层次数据共享和多范围同步操作带来的同步困难问题。这也可以认为是上个数据同步问题带来的后继问题:在异构系统中数据可能分布在不同位置的条件下,同步操作需要在众多的位置上保证共享数据的一致性,这使得同步操作的范围变得十分复杂。同时,在一些特定的加速设备中,例如GPU,可能还会有局部的硬件同步机制,这更加提高了在异构系统的同步操作的设计和实现难度。
     
    @@ -47,7 +48,7 @@ tags:
     
     从异构并行编程接口的功能角度上来说也可以分成两类:有些接口屏蔽了较多的异构并行编程细节,通常仅给程序员提供显式异构任务划分的机制,而数据分布和通信、同步等的工作由运行时系统负责完成,也有些接口将多数异构系统的硬件细节通过上述机制暴露给程序员使用,这在给编程带来更大自由度的同时带来了使用上的困难。
     
    -![83ee1d254d638536d0fb4197ff63e758.png](./heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.png)
    +![83ee1d254d638536d0fb4197ff63e758.webp](./heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.webp)
     
     ### 异构任务划分机制研究
     
    @@ -125,7 +126,7 @@ public class Result
     
     采用显示异步数据分布和通信机制的主要问题是普通程序员一般无法充分利用这些接口获得性能上的提升。这通常使用因为加速设备通常采用了大量的硬件加速机制,例如GPU的全局内存访存合并机制,这使得程序员如果没有为数据分配合理的存储位置或者设定足够多的线程,会使得加速的效果大打折扣。因此出现了针对这类显式控制语言的优化方法,例如`CUDA-lite`,这个运行时允许程序元在CUDA程序中加入简单的制导语句,数据分布的相关工作使用`CUDA-lite`的运行时系统完成,降低了CUDA程序的编写难度。
     
    -![628804b3fe95d39013ff55ae84516d14.png](./heterogeneous-programming-model/Screenshot_20241016_214139.png)
    +![628804b3fe95d39013ff55ae84516d14.png](./heterogeneous-programming-model/Screenshot_20241016_214139.webp)
     
     总结一下,为了解决异构系统带来的问题,异构并行编程接口具有如下三个特点:
     - 异构任务划分机制在传统并行编程模型的基础上增加了"异构特征描述"的维度,用于描述任务在不同设备上的分配情况;
    @@ -138,7 +139,7 @@ public class Result
     
     异构编程/运行时系统的任务映射机制主要有两种:一类是直接映射,即独立完成并行任务向异构平台映射的工作,另一种是间接映射,即需要借助其他异构编译和运行时系统协助来完成部分任务映射工作。直接映射系统一般在运行时系统中实现,而间接映射通过源到源变换和是运行时分析相结合的方式实现。
     
    -![](./heterogeneous-programming-model/Screenshot_20241016_214939.png)
    +![](./heterogeneous-programming-model/Screenshot_20241016_214939.webp)
     
     ### 异构编译/运行时优化
     
    @@ -456,13 +457,13 @@ std::vector> cudaCalculateMatrix(const std::vector OpenACCCpuCalculateMatrix(const std::vector& a, con
     
     oneAPI是Intel公司提出的一套异构并行编程框架,该框架致力于达成如下几个目标:(1)定义一个跨架构、跨制造商的统一开放软件平台;(2)允许同一套代码可以在不同硬件制造商和加速技术的硬件上运行;(3)提供一套覆盖多个编程领域的库API。为了实现这些目标,oneAPI同上文中已经提到过的开放编程标准SYCL紧密合作,oneAPI也提供了一个SYCL的编译器和运行时;同时oneAPI也提供了一系列API库,包括`oneDPL`、`oneDNN`、`oneTBB`和`oneMKL`等。
     
    -![image-20241103162259981](./heterogeneous-programming-model/image-20241103162259981.png)
    +![image-20241103162259981](./heterogeneous-programming-model/image-20241103162259981.webp)
     
     我对于oneAPI的理解就是Intel用来对标NVIDIA的CUDA的一套高性能编程工具箱。首先为了和NVIDIA完全闭源的CUDA形成鲜明的对比,Intel选择了OpenCL合作同时开发SYCL,当时也有可能是Intel知道自己的显卡技不如人,如果不兼容市面上其他的部件是没有出路的,同时为了和CUDA丰富的生态竞争,Intel再开发并开源了一系列的`oneXXX`。
     
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.png b/YaeBlog/source/posts/heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.png
    deleted file mode 100644
    index be2c3a8..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:024500276e29f53f65759819e1d9b6c826aa09e91f50e8821d06dcf070560a18
    -size 120181
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.webp b/YaeBlog/source/posts/heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.webp
    new file mode 100644
    index 0000000..79f47f1
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/83ee1d254d638536d0fb4197ff63e758.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:8c72b55977fde8f705070e25bb2b3a1c947a590931762b4b24fee61237541a13
    +size 42802
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.png b/YaeBlog/source/posts/heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.png
    deleted file mode 100644
    index d1388d4..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:5989d69e8fce9d83641034f103c8d4ef21a64f659d0e05660969819aa8bd6818
    -size 160181
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.webp b/YaeBlog/source/posts/heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.webp
    new file mode 100644
    index 0000000..92c9b83
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/9eb06d8be92ddef3db33e040163c67a7.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:9c2e4123d8743d38b3815c1e664eb6993995e887ca06aa0ff5ae1d17c5110b71
    +size 41612
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214139.png b/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214139.png
    deleted file mode 100644
    index 9b0bc73..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214139.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:01d884fe4a429c28e53ee586ab6c7318d6dcc5b92c1891d68d6ce7af4645f431
    -size 214725
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214139.webp b/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214139.webp
    new file mode 100644
    index 0000000..bf033cb
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214139.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:50958529b872e918e157e4d2ac88fcf75217d42fea5a1a15dfc1838da5fa224e
    +size 55040
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214939.png b/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214939.png
    deleted file mode 100644
    index ac22d37..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214939.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:1ec51d9872a4529d68c9b6afc9608d6b8007a57629ba5d117f7560527076d805
    -size 82164
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214939.webp b/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214939.webp
    new file mode 100644
    index 0000000..105b241
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/Screenshot_20241016_214939.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:3f18f28e72f57c13dc8cace6c4a37ecd62158cbfe5f3be8bb745205f2f9aa5fd
    +size 26892
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.png b/YaeBlog/source/posts/heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.png
    deleted file mode 100644
    index 7483275..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:f01cb683d410d1a45edc508a5ed3aeda6f1fcc208d0368a52504350cceb86c31
    -size 130457
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.webp b/YaeBlog/source/posts/heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.webp
    new file mode 100644
    index 0000000..9ef6e21
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/eab553f9e30d8d866a1ddd201b5e4c85.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:72f3ba7b8137833bf5eafe8b643f4b71bf6ee32a2a88245d488fb115037eae44
    +size 51792
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020142938110.png b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020142938110.png
    deleted file mode 100644
    index 7cd6736..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020142938110.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:ba84669fd872bf19b665aa673dcd1f03868ed4b4621b9abd460090cb3bb93b9a
    -size 171981
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020142938110.webp b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020142938110.webp
    new file mode 100644
    index 0000000..1fff52f
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020142938110.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:fbf1af9f0b6b77ce5ec5b66ec9e19703d5b60ad842c34902ec742bd4ee9eedc7
    +size 22878
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020155656219.png b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020155656219.png
    deleted file mode 100644
    index aa0ad1e..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020155656219.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:a4b20c42dbe0bf434b4224d23dbedee1175d5dfb21e7bfdde314e1af5b2a9586
    -size 194091
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020155656219.webp b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020155656219.webp
    new file mode 100644
    index 0000000..0322a6c
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241020155656219.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:0e7f3eb7bead9696d1251074e186b32f8cb5fad135301f6a94ee4e1fff906a4a
    +size 23790
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029123308139.png b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029123308139.png
    deleted file mode 100644
    index 774102e..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029123308139.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:9675685943c6124ffdc70023f19c8d043c484d72f62866e6b4e87eca270e0366
    -size 76309
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029123308139.webp b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029123308139.webp
    new file mode 100644
    index 0000000..b56a502
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029123308139.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:6c45d9932eb3b021a76cecb0367794c3c86862d7000d74a8dba4e78eca1834fe
    +size 43252
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029163654675.png b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029163654675.png
    deleted file mode 100644
    index 2d64a1a..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029163654675.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:4e38a5bdee31a5ddf11329d6b72ca8b1b2b119f5988166477e8e0ff63a1fa40d
    -size 195933
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029163654675.webp b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029163654675.webp
    new file mode 100644
    index 0000000..933432d
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241029163654675.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:94c23eb2289265f6e1181e3b4dd151bfeb052cb54b0ee762170fd49c4aa1cbcd
    +size 45140
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241103162259981.png b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241103162259981.png
    deleted file mode 100644
    index fa92f1c..0000000
    --- a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241103162259981.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:3ea27cccbb3e4de0b178fe7ea198f19e42603c90e254fca4356315998cf7dfce
    -size 38819
    diff --git a/YaeBlog/source/posts/heterogeneous-programming-model/image-20241103162259981.webp b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241103162259981.webp
    new file mode 100644
    index 0000000..02c9d76
    --- /dev/null
    +++ b/YaeBlog/source/posts/heterogeneous-programming-model/image-20241103162259981.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:64d6044e0cfd593262f335709a0a50f203da186c6967fda36e9d16b88ff69c21
    +size 17042
    diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture.md b/YaeBlog/source/posts/hpc-2025-cpu-architecture.md
    index a77cb8b..b454fc9 100644
    --- a/YaeBlog/source/posts/hpc-2025-cpu-architecture.md
    +++ b/YaeBlog/source/posts/hpc-2025-cpu-architecture.md
    @@ -6,6 +6,7 @@ tags:
     - 高性能计算
     ---
     
    +
     How to use the newly available transistors?
     
     
    @@ -54,11 +55,11 @@ Move the complexity of instruction scheduling from the CPU hardware to the softw
     - Assign instructions to the functional units.
     - Determine when instructions are initiated placed together into a single word.
     
    -![image-20250313184421305](./hpc-2025-cpu-architecture/image-20250313184421305.png)
    +![image-20250313184421305](./hpc-2025-cpu-architecture/image-20250313184421305.webp)
     
     Comparisons between different architecture:
     
    -![image-20250313184732892](./hpc-2025-cpu-architecture/image-20250313184732892.png)
    +![image-20250313184732892](./hpc-2025-cpu-architecture/image-20250313184732892.webp)
     
     ## Multi-Core Processor Gala
     
    @@ -89,7 +90,7 @@ Hardware approaches to multithreading:
     
     - A variation on fine-grained multithreading
     
    -![image-20250313190913475](./hpc-2025-cpu-architecture/image-20250313190913475.png)
    +![image-20250313190913475](./hpc-2025-cpu-architecture/image-20250313190913475.webp)
     
     ## Data Parallelism: Vector Processors
     
    diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.png b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.png
    deleted file mode 100644
    index 89925dc..0000000
    --- a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:1e3183c5ad4ee11b6dd924a0ed85ed9a13d3d61a78de741fcb649fe14def6473
    -size 158027
    diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.webp b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.webp
    new file mode 100644
    index 0000000..88093ce
    --- /dev/null
    +++ b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184421305.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:da1fa5c5f18b966c14647f38cdc0007495a02bfe3d0d4739c6d5fe6ff8bf3b85
    +size 18176
    diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.png b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.png
    deleted file mode 100644
    index 90cf137..0000000
    --- a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:e9c4a4cee93475a3d73f5ed8bc97e5978ae5f099c754b4e94c2b8e5a1a2586cc
    -size 776759
    diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.webp b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.webp
    new file mode 100644
    index 0000000..468e858
    --- /dev/null
    +++ b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313184732892.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:e0acec6c138987bd7783f798c096f02cc46745435e02afd476c9ea1c501a7fd4
    +size 46450
    diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.png b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.png
    deleted file mode 100644
    index 6f3672e..0000000
    --- a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:65afb4d57b7cd63940786cf43eb5b455e6ab9b19c48257f599a01dc36616352f
    -size 239063
    diff --git a/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.webp b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.webp
    new file mode 100644
    index 0000000..73273c7
    --- /dev/null
    +++ b/YaeBlog/source/posts/hpc-2025-cpu-architecture/image-20250313190913475.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:1bf11c172400f6a5b8eb54e3922baedbbc38f9da72e4d852898515106d992b99
    +size 15492
    diff --git a/YaeBlog/source/posts/install-pytorch.md b/YaeBlog/source/posts/install-pytorch.md
    index 11f8314..04304a4 100644
    --- a/YaeBlog/source/posts/install-pytorch.md
    +++ b/YaeBlog/source/posts/install-pytorch.md
    @@ -1,12 +1,11 @@
     ---
     title: 安装pytorch,来有深度的学习
    -date: 2021-11-13 13:21:53
    -toc: true
    -tags: 
    -        - 技术笔记 
    -typora-root-url: 安装pytorch,来有深度的学习
    +date: 2021-11-13T13:21:53.0000000
    +tags:
    +- 技术笔记
     ---
     
    +
     鄙人在下不才我精通深度学习框架的安装和卸载。
     
     
    @@ -40,7 +39,7 @@ typora-root-url: 安装pytorch,来有深度的学习
     
     我们访问`miniconda`的[官方网站](https://docs.conda.io/en/latest/miniconda.html) ,下滑找到最新的`miniconda`安装包下载链接(Latest Miniconda Installer Links)。 
     
    -![](1.png)
    +![](1.webp)
     
     按照自己的电脑系统下载相应的安装包就可以了。也不大,就50M出头的样子。
     
    @@ -48,7 +47,7 @@ typora-root-url: 安装pytorch,来有深度的学习
     
     安装完成之后,我们就会发现自己的开始菜单里多出来了两个快捷方式,
     
    -![](2.png)
    +![](2.webp)
     在上面的那个快捷方式**Anaconda Prompt**是运行含有`conda`命令的CMD的快捷方式,而下面那个**Anaconda Power Shell Prompt**是运行含有`conda`命令的Power shell界面的快捷方式。因为我个人对于power shell更加的熟悉我们就是用下面的那个快捷方式。   
     
     这个时候肯定会有人好奇,竟然你都用power shell了,我们为啥不直接使用系统里已经有了的power shell,而要启动这里的这个显得有点奇怪的power shell呢?     
    @@ -57,7 +56,7 @@ typora-root-url: 安装pytorch,来有深度的学习
     
     好,我们运行**Anaconda Power Shell Prompt**,打开了这个窗口
     
    -![](3.png)
    +![](3.webp)
     
     这个窗口的出现就标志着我们已经把`conda`正确的安装在我们的电脑之中了。
     
    @@ -160,7 +159,7 @@ conda update python
     
     ### 使用conda安装pytorch
     我们访问pytorch的[官方网站](https://pytorch.org/),我们可以轻松在首页就找到这个表格
    -![](4.png)
    +![](4.webp)
     先简单介绍一下这个表格的每一行,第一行是选择我们安装`pytorch`的版本,是稳定版,预览版,还是长期支持版。第二行是选择我们索要下载的操作系统,这里就默认大家都是用Windows了,第三行是选择我们安装`pytorch`的方式,可以看见有我们刚学习的`conda`,我们很熟悉的`pip`, 还有两种其他的方式。我们选择`conda`。第三行是我们所使用的语言,我们自然选择`python`。然后是第四行,“Compute Platform”, 如果直译的话是计算平台,就是我们选择用来计算的设备。这里就是又一个新的知识点了。一般来说,我们在使用电脑时,都是使用CPU作为计算的主力,显卡(GPU)一般只是用来输出图像,但在深度学习出现后,人们发现显卡原本专精于图形计算的计算力也很适用于深度学习中人工神经网络的计算。为了能够使用GPU的算力,而不是让它仅仅输出图像,我们就得下载相关的工具,这个工具就是CUDA。当然,这种比较强大的能力并不是只要下载一个CUDA就可以拥有的,~~你还得有钱~~你还得拥有一块英伟达的显卡,如果你没有显卡,那你就只能老老实实的使用自己的CPU进行计算了,在表格的第四行选择CPU。而对于我们~~土豪~~有显卡的同学,我们得先确定确定自己电脑上CUDA的版本,打开我们的老朋友power shell,输入
     
     ```
    diff --git a/YaeBlog/source/posts/install-pytorch/1.png b/YaeBlog/source/posts/install-pytorch/1.png
    deleted file mode 100644
    index 1ea1f95..0000000
    --- a/YaeBlog/source/posts/install-pytorch/1.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:d1067846d8b27357d9c1be6d25d768137984074de8077445ed0347393bea76d6
    -size 90203
    diff --git a/YaeBlog/source/posts/install-pytorch/1.webp b/YaeBlog/source/posts/install-pytorch/1.webp
    new file mode 100644
    index 0000000..b7c4f24
    --- /dev/null
    +++ b/YaeBlog/source/posts/install-pytorch/1.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:7399c6fd6cdf7689105607739c5b67432f6f723508b3db2dc5c370219074533f
    +size 30520
    diff --git a/YaeBlog/source/posts/install-pytorch/2.png b/YaeBlog/source/posts/install-pytorch/2.png
    deleted file mode 100644
    index 73551bd..0000000
    --- a/YaeBlog/source/posts/install-pytorch/2.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:13206dc6972a3ce3f4b23d723f96b6ada5e34f87a051e9de446f2a07c375932d
    -size 38074
    diff --git a/YaeBlog/source/posts/install-pytorch/2.webp b/YaeBlog/source/posts/install-pytorch/2.webp
    new file mode 100644
    index 0000000..e8a8576
    --- /dev/null
    +++ b/YaeBlog/source/posts/install-pytorch/2.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:a4a236af737ef2fc99e7447b6da73e5fe7bd4687823235535081592e6bdbfbaf
    +size 20334
    diff --git a/YaeBlog/source/posts/install-pytorch/3.png b/YaeBlog/source/posts/install-pytorch/3.png
    deleted file mode 100644
    index 66ee198..0000000
    --- a/YaeBlog/source/posts/install-pytorch/3.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:e813716f7b1e945e10942c55a7d5ea30ea57a0d374255ed110ed90ffea03633a
    -size 12042
    diff --git a/YaeBlog/source/posts/install-pytorch/3.webp b/YaeBlog/source/posts/install-pytorch/3.webp
    new file mode 100644
    index 0000000..c1ed6ff
    --- /dev/null
    +++ b/YaeBlog/source/posts/install-pytorch/3.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:7f36e3aeb862b586999a2f0a456da844c7472d3f86ef8dd7a562218bd2802698
    +size 2242
    diff --git a/YaeBlog/source/posts/install-pytorch/4.png b/YaeBlog/source/posts/install-pytorch/4.png
    deleted file mode 100644
    index 062df7d..0000000
    --- a/YaeBlog/source/posts/install-pytorch/4.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:0731b5398952c034de376b9db8980c3bd8a9d8a877a2bf0bb9991015926d80ee
    -size 39134
    diff --git a/YaeBlog/source/posts/install-pytorch/4.webp b/YaeBlog/source/posts/install-pytorch/4.webp
    new file mode 100644
    index 0000000..365f7b2
    --- /dev/null
    +++ b/YaeBlog/source/posts/install-pytorch/4.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:037a17878e6494a6beca6e4fd4905b1a6ca025d6ccd2e3754a3806bfb114973f
    +size 15894
    diff --git a/YaeBlog/source/posts/laptop-for-computer.md b/YaeBlog/source/posts/laptop-for-computer.md
    index 022a018..39e33fa 100644
    --- a/YaeBlog/source/posts/laptop-for-computer.md
    +++ b/YaeBlog/source/posts/laptop-for-computer.md
    @@ -1,12 +1,12 @@
     ---
     title: 大学生用啥配置——计算机专业
    +date: 2022-06-13T16:17:27.0000000
     tags:
    -  - 杂谈
    -typora-root-url: laptop-for-computer
    -date: 2022-06-13 16:17:27
    +- 杂谈
     ---
     
     
    +
     > 本文是应B站UP主[远古时代装机猿](https://space.bilibili.com/35359510)发起的[大学生用啥配置](https://www.bilibili.com/video/BV1kZ4y1i7Le)公益活动而写
     >
     > 目前某不知名211大学计算机专业在读
    @@ -52,7 +52,7 @@ date: 2022-06-13 16:17:27
     
     目前的性能占用如图所示
     
    -![](c.png)
    +![](c.webp)
     
     在编译代码时CPU占用会有一个短时的提升,不过由于目前编写的程序都较为简单,编译转瞬就完成了。
     
    @@ -62,7 +62,7 @@ date: 2022-06-13 16:17:27
     
     性能的占用如图所示
     
    -![](clion.png)
    +![](clion.webp)
     
     在刚打开项目和编译整个项目时CPU的占用会上升,但在完成之后,CPU的占用就会回落。
     
    @@ -74,7 +74,7 @@ date: 2022-06-13 16:17:27
     
     性能占用如图所示
     
    -![](csharp.png)
    +![](csharp.webp)
     
     #### 写网站
     
    @@ -82,7 +82,7 @@ date: 2022-06-13 16:17:27
     
     性能占用如图所示:
     
    -![](web.png)
    +![](web.webp)
     
     ### 软件的运行需求分析
     
    diff --git a/YaeBlog/source/posts/laptop-for-computer/c.png b/YaeBlog/source/posts/laptop-for-computer/c.png
    deleted file mode 100644
    index c32ec4b..0000000
    --- a/YaeBlog/source/posts/laptop-for-computer/c.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:0f321d75e6ea913d68ab8a74206bd3a7dd086451fc69fde4339d39f8127de259
    -size 139253
    diff --git a/YaeBlog/source/posts/laptop-for-computer/c.webp b/YaeBlog/source/posts/laptop-for-computer/c.webp
    new file mode 100644
    index 0000000..3ede867
    --- /dev/null
    +++ b/YaeBlog/source/posts/laptop-for-computer/c.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:d93cb06f6bb8472848519ff59e336d47024159570100f13637b10b91e9dc7ba3
    +size 103014
    diff --git a/YaeBlog/source/posts/laptop-for-computer/clion.png b/YaeBlog/source/posts/laptop-for-computer/clion.png
    deleted file mode 100644
    index 7b2dd2d..0000000
    --- a/YaeBlog/source/posts/laptop-for-computer/clion.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:85f25d0fc05c7fc6e0c50dbda02d053f9127e3510161272f2645333d4b7a9aa2
    -size 147636
    diff --git a/YaeBlog/source/posts/laptop-for-computer/clion.webp b/YaeBlog/source/posts/laptop-for-computer/clion.webp
    new file mode 100644
    index 0000000..830f8cc
    --- /dev/null
    +++ b/YaeBlog/source/posts/laptop-for-computer/clion.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:f991d47571c3f8e4eb1d8abf35785fd112f0d82ee56c1993cefc8b530a797b5f
    +size 107082
    diff --git a/YaeBlog/source/posts/laptop-for-computer/csharp.png b/YaeBlog/source/posts/laptop-for-computer/csharp.png
    deleted file mode 100644
    index 988e1fd..0000000
    --- a/YaeBlog/source/posts/laptop-for-computer/csharp.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:415601b9824037f6d8c0f8b4759c0aa2afa4eafd02212c07e766727996ef30ee
    -size 151640
    diff --git a/YaeBlog/source/posts/laptop-for-computer/csharp.webp b/YaeBlog/source/posts/laptop-for-computer/csharp.webp
    new file mode 100644
    index 0000000..c592760
    --- /dev/null
    +++ b/YaeBlog/source/posts/laptop-for-computer/csharp.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:9887563cc3b3b76b2f5b305583ff0b6368277347a6f5afc250d46bc0624d42e0
    +size 109156
    diff --git a/YaeBlog/source/posts/laptop-for-computer/web.png b/YaeBlog/source/posts/laptop-for-computer/web.png
    deleted file mode 100644
    index 4468a3e..0000000
    --- a/YaeBlog/source/posts/laptop-for-computer/web.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:8b889b41f828d79416eb3ccb033fe8a0ab90953aaddd2cb36ea333c560e87195
    -size 146745
    diff --git a/YaeBlog/source/posts/laptop-for-computer/web.webp b/YaeBlog/source/posts/laptop-for-computer/web.webp
    new file mode 100644
    index 0000000..f0ea85a
    --- /dev/null
    +++ b/YaeBlog/source/posts/laptop-for-computer/web.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:786ab13b2c81724b80ce0aa82ecdf54aa5e469f538e1c0484d5f5038ed21d910
    +size 105566
    diff --git a/YaeBlog/source/posts/llvm-naive-0.md b/YaeBlog/source/posts/llvm-naive-0.md
    index 100a501..4ec7eb4 100644
    --- a/YaeBlog/source/posts/llvm-naive-0.md
    +++ b/YaeBlog/source/posts/llvm-naive-0.md
    @@ -7,6 +7,7 @@ tags:
     - 技术笔记
     ---
     
    +
     为什么说LLVM是神?
     
     
    @@ -55,7 +56,7 @@ clang hello.c -o hello
     
     编译完成之后可以正常执行:
     
    -![image-20240819213039409](./llvm-naive-0/image-20240819213039409.png)
    +![image-20240819213039409](./llvm-naive-0/image-20240819213039409.webp)
     
     然后尝试将这个C语言文件编译为LLVM的字节码形式:
     
    @@ -71,7 +72,7 @@ clang -O3 -emit-llvm hello.c -c -o hello.bc
     lli hello.bc
     ```
     
    -![image-20240819213624927](./llvm-naive-0/image-20240819213624927.png)
    +![image-20240819213624927](./llvm-naive-0/image-20240819213624927.webp)
     
     可以使用反编译器将字节码转换为人类可读的文本形式:
     
    @@ -152,7 +153,7 @@ main:                                   # @main
     
     下面这张图展示了LLVM中各种文件的转换关系和使用的对应工具。
     
    -![image-20240820221413791](./llvm-naive-0/image-20240820221413791.png)
    +![image-20240820221413791](./llvm-naive-0/image-20240820221413791.webp)
     
     ## LLVM IR
     
    @@ -268,5 +269,5 @@ entry:
     
     使用`lli`解释器可以直接运行这段代码:
     
    -![image-20240825171858276](./llvm-naive-0/image-20240825171858276.png)
    +![image-20240825171858276](./llvm-naive-0/image-20240825171858276.webp)
     
    diff --git a/YaeBlog/source/posts/llvm-naive-0/image-20240819213039409.png b/YaeBlog/source/posts/llvm-naive-0/image-20240819213039409.png
    deleted file mode 100644
    index 39a5267..0000000
    --- a/YaeBlog/source/posts/llvm-naive-0/image-20240819213039409.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:ce8ce7601d7b1d041f25d781622657b2864be26f632bf30740f800e3f15ab63d
    -size 14787
    diff --git a/YaeBlog/source/posts/llvm-naive-0/image-20240819213039409.webp b/YaeBlog/source/posts/llvm-naive-0/image-20240819213039409.webp
    new file mode 100644
    index 0000000..640b62d
    --- /dev/null
    +++ b/YaeBlog/source/posts/llvm-naive-0/image-20240819213039409.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:672011acf34f27be68cb1cae19598aa4f0da697a365b025b60a84a5891563098
    +size 8006
    diff --git a/YaeBlog/source/posts/llvm-naive-0/image-20240819213624927.png b/YaeBlog/source/posts/llvm-naive-0/image-20240819213624927.png
    deleted file mode 100644
    index c9f1cdd..0000000
    --- a/YaeBlog/source/posts/llvm-naive-0/image-20240819213624927.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:bb82eae62bca2af0eb0c42c0e46a314a1663a161d5573ccd938a5c1d1916e67e
    -size 9215
    diff --git a/YaeBlog/source/posts/llvm-naive-0/image-20240819213624927.webp b/YaeBlog/source/posts/llvm-naive-0/image-20240819213624927.webp
    new file mode 100644
    index 0000000..2f52fec
    --- /dev/null
    +++ b/YaeBlog/source/posts/llvm-naive-0/image-20240819213624927.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:4ff4a658ea5e4805c2193276b1fb4cd688865f0831bb76fc371e26137572bc3d
    +size 6892
    diff --git a/YaeBlog/source/posts/llvm-naive-0/image-20240820221413791.png b/YaeBlog/source/posts/llvm-naive-0/image-20240820221413791.png
    deleted file mode 100644
    index 5d1c75b..0000000
    --- a/YaeBlog/source/posts/llvm-naive-0/image-20240820221413791.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:18ff733fbd8f02ece52b3b7fd22f2df94b209f11db9af082125ad448bee6cf07
    -size 81957
    diff --git a/YaeBlog/source/posts/llvm-naive-0/image-20240820221413791.webp b/YaeBlog/source/posts/llvm-naive-0/image-20240820221413791.webp
    new file mode 100644
    index 0000000..94fc168
    --- /dev/null
    +++ b/YaeBlog/source/posts/llvm-naive-0/image-20240820221413791.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:68df83cedd473725b0379fa786739408f868863b6e205a401a4cd4335812f653
    +size 33988
    diff --git a/YaeBlog/source/posts/llvm-naive-0/image-20240825171858276.png b/YaeBlog/source/posts/llvm-naive-0/image-20240825171858276.png
    deleted file mode 100644
    index 6f13bdf..0000000
    --- a/YaeBlog/source/posts/llvm-naive-0/image-20240825171858276.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:f198e93178db7a98fd7e2c0ffb3fc387d5b8793b37bed1cd0068d9f177a49ba6
    -size 13107
    diff --git a/YaeBlog/source/posts/llvm-naive-0/image-20240825171858276.webp b/YaeBlog/source/posts/llvm-naive-0/image-20240825171858276.webp
    new file mode 100644
    index 0000000..f9658b7
    --- /dev/null
    +++ b/YaeBlog/source/posts/llvm-naive-0/image-20240825171858276.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:ba4bd9fbd90f0f81bb1fd32e54d8c249a4f7a9fc4ea2b2ab968644e49132eedc
    +size 6948
    diff --git a/YaeBlog/source/posts/minecraft-wayland.md b/YaeBlog/source/posts/minecraft-wayland.md
    index 645915f..bfdf931 100644
    --- a/YaeBlog/source/posts/minecraft-wayland.md
    +++ b/YaeBlog/source/posts/minecraft-wayland.md
    @@ -1,11 +1,12 @@
     ---
     title: 让Minecraft运行在Wayland下
    +date: 2024-01-12T20:10:06.0000000
     tags:
    -  - Linux
    -  - 生活小妙招
    -date: 2024-1-12 20:10:06
    +- Linux
    +- 生活小妙招
     ---
     
    +
     让Minecraft游戏使用`Wayland`显示协议。
     
     
    @@ -127,15 +128,15 @@ export __GL_THREADED_OPTIMIZATIONS=0
     
     然后就是设置Minecraft使用我们自行编译的图形库,需要设置`java`启动参数,我使用的[hmcl](https://github.com/huanghongxun/HMCL)这款启动器,可以在设置里面很方便的设置启动参数:
     
    -![image-20240105212744116](./minecraft-wayland/image-20240105212744116.png)
    +![image-20240105212744116](./minecraft-wayland/image-20240105212744116.webp)
     
     但是,如果你使用的是`nvidia`显卡,这里还会遇到一个问题:
     
    -![image-20240105213439528](./minecraft-wayland/image-20240105213439528.png)
    +![image-20240105213439528](./minecraft-wayland/image-20240105213439528.webp)
     
     这个问题从一些资料显示仍然是老黄整的好活:
     
    -![image-20240105213942445](./minecraft-wayland/image-20240105213942445.png)
    +![image-20240105213942445](./minecraft-wayland/image-20240105213942445.webp)
     
     设置环境变量解决,再次强烈推荐[hmcl](https://github.com/huanghongxun/HMCL)启动器,可以方便的设置环境变量。
     
    diff --git a/YaeBlog/source/posts/minecraft-wayland/image-20240105212744116.png b/YaeBlog/source/posts/minecraft-wayland/image-20240105212744116.png
    deleted file mode 100644
    index 4d2005d..0000000
    --- a/YaeBlog/source/posts/minecraft-wayland/image-20240105212744116.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:0308351e4a4a5387ea689fb5841e89578c5dd8e31a8f173c1842b4fa0819b537
    -size 57285
    diff --git a/YaeBlog/source/posts/minecraft-wayland/image-20240105212744116.webp b/YaeBlog/source/posts/minecraft-wayland/image-20240105212744116.webp
    new file mode 100644
    index 0000000..920799e
    --- /dev/null
    +++ b/YaeBlog/source/posts/minecraft-wayland/image-20240105212744116.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:4b88af664411089105ab95d3c2e8678796dab177484664251c073eef31356f2f
    +size 47110
    diff --git a/YaeBlog/source/posts/minecraft-wayland/image-20240105213439528.png b/YaeBlog/source/posts/minecraft-wayland/image-20240105213439528.png
    deleted file mode 100644
    index bbcb1e0..0000000
    --- a/YaeBlog/source/posts/minecraft-wayland/image-20240105213439528.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:b4d43e3eb2635648c755d8ab724a84571fb107f2b627495abdaaa7c058f313be
    -size 12603
    diff --git a/YaeBlog/source/posts/minecraft-wayland/image-20240105213439528.webp b/YaeBlog/source/posts/minecraft-wayland/image-20240105213439528.webp
    new file mode 100644
    index 0000000..2e377fb
    --- /dev/null
    +++ b/YaeBlog/source/posts/minecraft-wayland/image-20240105213439528.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:9c2489337160ab1eb5620d5cdee9d927b5a4dab2d90d5d6c571a7641acecbe45
    +size 7410
    diff --git a/YaeBlog/source/posts/minecraft-wayland/image-20240105213942445.png b/YaeBlog/source/posts/minecraft-wayland/image-20240105213942445.png
    deleted file mode 100644
    index af1237d..0000000
    --- a/YaeBlog/source/posts/minecraft-wayland/image-20240105213942445.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:8379ee6733e6753da5ed2acddfcdff39b60e51d6f04c5e3c577e07ef6792bfc9
    -size 153864
    diff --git a/YaeBlog/source/posts/minecraft-wayland/image-20240105213942445.webp b/YaeBlog/source/posts/minecraft-wayland/image-20240105213942445.webp
    new file mode 100644
    index 0000000..dee85cb
    --- /dev/null
    +++ b/YaeBlog/source/posts/minecraft-wayland/image-20240105213942445.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:40096bb91cfc78eb62c94487ca8fb8506e7390d4800cd07aee24b884801cb70a
    +size 65760
    diff --git a/YaeBlog/source/posts/mlir-standalone.md b/YaeBlog/source/posts/mlir-standalone.md
    index 7943448..578c77f 100644
    --- a/YaeBlog/source/posts/mlir-standalone.md
    +++ b/YaeBlog/source/posts/mlir-standalone.md
    @@ -6,6 +6,7 @@ tags:
     - LLVM
     ---
     
    +
     MLIR是多层次中间表示形式(Multi-Level Intermediate Representation),是LLVM项目中提供的一项新编译器开发基础设施,使得编译器开发者能够在源代码和可执行代码之间定义多层IR来保留程序信息指导编译优化。本博客指导如何创建一个独立(out-of-tree)的MLIR项目。
     
     
    @@ -35,7 +36,7 @@ cd build
     cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="/home/ricardo/.local/share/llvm-20.1.0" -DLLVM_ENABLE_PROJECTS="llvm;clang;mlir" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_INSTALL_UTILS=true ../llvm
     ```
     
    -![image-20250319192618697](./mlir-standalone/image-20250319192618697.png)
    +![image-20250319192618697](./mlir-standalone/image-20250319192618697.webp)
     
     生成构建文件之后使用`ninja`进行构建。
     
    @@ -43,7 +44,7 @@ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="/home/ricardo/
     ninja
     ```
     
    -![image-20250319194742171](./mlir-standalone/image-20250319194742171.png)
    +![image-20250319194742171](./mlir-standalone/image-20250319194742171.webp)
     
     构建在我的i5-13600K上大约需要20分钟。
     
    @@ -93,7 +94,7 @@ cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir  ..
     
     可以顺利通过编译。
     
    -![image-20250319202218503](./mlir-standalone/image-20250319202218503.png)
    +![image-20250319202218503](./mlir-standalone/image-20250319202218503.webp)
     
     ### 启用测试
     
    @@ -106,7 +107,7 @@ cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir  ..
     
     很好顺利报错,报错的提示是缺失`FileCheck`、`count`和`not`。
     
    -![image-20250319202553644](./mlir-standalone/image-20250319202553644.png)
    +![image-20250319202553644](./mlir-standalone/image-20250319202553644.webp)
     
     那么按照`README.md`中的提示添加上来自构建目录的`llvm-lit`会怎么样呢?
     
    @@ -120,7 +121,7 @@ cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=$LLVM_BUI
     
     经过对于LLVM文档的仔细研究,发现原来是没有启动这个变量:
     
    -![image-20250319204057832](./mlir-standalone/image-20250319204057832.png)
    +![image-20250319204057832](./mlir-standalone/image-20250319204057832.webp)
     
     遂修改最初的LLVM编译指令。
     
    @@ -130,7 +131,7 @@ cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=$LLVM_BUI
     ninja test-standalone
     ```
     
    -![image-20250319204522857](./mlir-standalone/image-20250319204522857.png)
    +![image-20250319204522857](./mlir-standalone/image-20250319204522857.webp)
     
     不过这个还是有一点令我不是特别满意,这依赖了一个来自于构建目录的工具`llvm-lit`,如果我编译安装的时候眼疾手快的删除了编译目录不就完蛋了。而且我都**standalone**了还依赖似乎有点说不过去?
     
    @@ -160,4 +161,4 @@ cmake -G Ninja -DMLIR_DIR=$LLVM_DIR/lib/cmake/mlir -DLLVM_EXTERNAL_LIT=$(which l
     ninja test-standalone
     ```
     
    -![image-20250319205520649](./mlir-standalone/image-20250319205520649.png)
    +![image-20250319205520649](./mlir-standalone/image-20250319205520649.webp)
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319192618697.png b/YaeBlog/source/posts/mlir-standalone/image-20250319192618697.png
    deleted file mode 100644
    index 32d3974..0000000
    --- a/YaeBlog/source/posts/mlir-standalone/image-20250319192618697.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:dce1e3bf8f40cb65ab1a75bd2640d8f40573c977de20c748d9ece4d3efbeb791
    -size 76812
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319192618697.webp b/YaeBlog/source/posts/mlir-standalone/image-20250319192618697.webp
    new file mode 100644
    index 0000000..267dae8
    --- /dev/null
    +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319192618697.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:f5af895b246f31588b64715d5b8efe265fc35774c7b534163de99fb07970f48c
    +size 7738
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319194742171.png b/YaeBlog/source/posts/mlir-standalone/image-20250319194742171.png
    deleted file mode 100644
    index c03dbce..0000000
    --- a/YaeBlog/source/posts/mlir-standalone/image-20250319194742171.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:41c2aae547c0d4ab2ccd520bc7bb914222a95dfdb879f0166e624fa7edbccb79
    -size 26105
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319194742171.webp b/YaeBlog/source/posts/mlir-standalone/image-20250319194742171.webp
    new file mode 100644
    index 0000000..870f11a
    --- /dev/null
    +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319194742171.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:8916648fa02bdef28250f39ae36e2d0fe894ac7bb653f574e85ca2898beaf974
    +size 8086
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319202218503.png b/YaeBlog/source/posts/mlir-standalone/image-20250319202218503.png
    deleted file mode 100644
    index a797831..0000000
    --- a/YaeBlog/source/posts/mlir-standalone/image-20250319202218503.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:270725669a377f83b2c134272f0f5489411ef549d8997600b3acb1bd37aac85f
    -size 19846
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319202218503.webp b/YaeBlog/source/posts/mlir-standalone/image-20250319202218503.webp
    new file mode 100644
    index 0000000..57ddb5a
    --- /dev/null
    +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319202218503.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:3440df949ceee03748b6c0519610a67aad977a1d0101447d7b6c39c5dbb632df
    +size 9598
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319202553644.png b/YaeBlog/source/posts/mlir-standalone/image-20250319202553644.png
    deleted file mode 100644
    index d8c5e37..0000000
    --- a/YaeBlog/source/posts/mlir-standalone/image-20250319202553644.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:f0398124dda06e47933e7631f3c1363a21eb3b32db05c709ba157819da69fa6b
    -size 114678
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319202553644.webp b/YaeBlog/source/posts/mlir-standalone/image-20250319202553644.webp
    new file mode 100644
    index 0000000..6c682a7
    --- /dev/null
    +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319202553644.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:33826ceb4246b65b55a44fe3d1c7e9dcb72a5ee7e917fc6c18e944e9e7a3e1aa
    +size 21426
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319204057832.png b/YaeBlog/source/posts/mlir-standalone/image-20250319204057832.png
    deleted file mode 100644
    index 57dff91..0000000
    --- a/YaeBlog/source/posts/mlir-standalone/image-20250319204057832.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:bb677444bdc074b20d795d1ab134344e73082b3be206cb5507eb8ddc9677b679
    -size 18618
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319204057832.webp b/YaeBlog/source/posts/mlir-standalone/image-20250319204057832.webp
    new file mode 100644
    index 0000000..865a57c
    --- /dev/null
    +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319204057832.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:0a941d0a6f4e40bb4002e082541ba96fbbc615e3cbc1ecae0f7e376090a802c6
    +size 14166
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319204522857.png b/YaeBlog/source/posts/mlir-standalone/image-20250319204522857.png
    deleted file mode 100644
    index 224d2ea..0000000
    --- a/YaeBlog/source/posts/mlir-standalone/image-20250319204522857.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:f0f8813f97e766aaf1f0fe67f1ff5d4c21d4584ed677de08da2edc9bca222a1f
    -size 48105
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319204522857.webp b/YaeBlog/source/posts/mlir-standalone/image-20250319204522857.webp
    new file mode 100644
    index 0000000..db2dd15
    --- /dev/null
    +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319204522857.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:65136396fea6c22f1775523bd8c1e762c0ab6adf366e66c5b735ce1afcd1607f
    +size 15996
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319205520649.png b/YaeBlog/source/posts/mlir-standalone/image-20250319205520649.png
    deleted file mode 100644
    index 910ed77..0000000
    --- a/YaeBlog/source/posts/mlir-standalone/image-20250319205520649.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:07320dbbb252cbf10e3ccde1b2ed06a1aed35dd128932cebdba387d9f88e599a
    -size 37427
    diff --git a/YaeBlog/source/posts/mlir-standalone/image-20250319205520649.webp b/YaeBlog/source/posts/mlir-standalone/image-20250319205520649.webp
    new file mode 100644
    index 0000000..2e527e0
    --- /dev/null
    +++ b/YaeBlog/source/posts/mlir-standalone/image-20250319205520649.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:c680ecc320ce7801e0e54c149d9ef619019405d1f0ba1031c5c5605d40f736c3
    +size 10614
    diff --git a/YaeBlog/source/posts/parser-combinator-performance.md b/YaeBlog/source/posts/parser-combinator-performance.md
    index fe49651..c13a3cf 100644
    --- a/YaeBlog/source/posts/parser-combinator-performance.md
    +++ b/YaeBlog/source/posts/parser-combinator-performance.md
    @@ -1,11 +1,12 @@
     ---
     title: 解析器组合子和LR(1)分析方法的性能比较
    +date: 2024-08-19T14:31:00.0000000
     tags:
    -  - 编译原理
    -  - 技术笔记
    -date: 2024-08-19 14:31:00
    +- 编译原理
    +- 技术笔记
     ---
     
    +
     在使用解析器组合子编写编译器前端时,其与LR分析方法之间的性能差距是开发人员关心的重点问题之一。
     
     
    @@ -146,7 +147,7 @@ public class GrammarParserBenchmark
     
     在对原始数据进行数据处理之后我们绘制了如下的图。图中的横轴是输入测试文件的编号,图中的纵轴是`CanonSharp`解析器和`Canon`解析器运行时间的比值。从图中可以看出`CanonSharp`解析器的运行时间大约是`Canon`解析器运行时间的65到75倍,在某些极端的情况下可能会达到90倍。
     
    -![image-20240819140523087](./parser-combinator-performance/image-20240819140523087.png)
    +![image-20240819140523087](./parser-combinator-performance/image-20240819140523087.webp)
     
     ## 结论
     
    diff --git a/YaeBlog/source/posts/parser-combinator-performance/image-20240819140523087.png b/YaeBlog/source/posts/parser-combinator-performance/image-20240819140523087.png
    deleted file mode 100644
    index a06cdf6..0000000
    --- a/YaeBlog/source/posts/parser-combinator-performance/image-20240819140523087.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:a34416d22a7b254db9904d2bb5ec5b7082a76df514c0316812f3477618f5c5eb
    -size 30728
    diff --git a/YaeBlog/source/posts/parser-combinator-performance/image-20240819140523087.webp b/YaeBlog/source/posts/parser-combinator-performance/image-20240819140523087.webp
    new file mode 100644
    index 0000000..b621851
    --- /dev/null
    +++ b/YaeBlog/source/posts/parser-combinator-performance/image-20240819140523087.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:a78ceaf7a4bab37963057c792aae1373385042c3dce61dca04f288d3b3458037
    +size 14538
    diff --git a/YaeBlog/source/posts/parser-combinator.md b/YaeBlog/source/posts/parser-combinator.md
    index baf2b98..cc2130a 100644
    --- a/YaeBlog/source/posts/parser-combinator.md
    +++ b/YaeBlog/source/posts/parser-combinator.md
    @@ -1,12 +1,13 @@
     ---
     title: 使用Parser Combinator编写编译器前端
    +date: 2024-08-13T18:09:10.0000000
     tags:
    -  - 编译原理
    -  - C#
    -  - 技术笔记
    -date: 2024-08-13 18:09:10
    +- 编译原理
    +- C#
    +- 技术笔记
     ---
     
    +
     在函数式编程思想的指导下,使用Parser Combinator编写编译器前端。
     
     
    @@ -402,7 +403,7 @@ public class C {
     
     
     
    -![image-20240813214315576](./parser-combinator/image-20240813214315576.png)
    +![image-20240813214315576](./parser-combinator/image-20240813214315576.webp)
     
     跳过组合子的实现则是使用我们之前提过的修改组合子(Fix Parser)进行的。
     
    @@ -454,9 +455,9 @@ public static Parser> Quote(thi
     
     Pascal-S语言的词法约定如下所示:
     
    -![image-20240813220521028](./parser-combinator/image-20240813220521028.png)
    +![image-20240813220521028](./parser-combinator/image-20240813220521028.webp)
     
    -![image-20240813220530717](./parser-combinator/image-20240813220530717.png)
    +![image-20240813220530717](./parser-combinator/image-20240813220530717.webp)
     
     据此,我们可以开始编写对应的词法分析器。首先给出一个词法令牌的规定,将词法令牌分类为:
     
    diff --git a/YaeBlog/source/posts/parser-combinator/image-20240813214315576.png b/YaeBlog/source/posts/parser-combinator/image-20240813214315576.png
    deleted file mode 100644
    index 3cae470..0000000
    --- a/YaeBlog/source/posts/parser-combinator/image-20240813214315576.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:c8565dc29005087be0fff55f3c56331cdcc70cc977ccbf9ef8ae047f90e5b8f3
    -size 29655
    diff --git a/YaeBlog/source/posts/parser-combinator/image-20240813214315576.webp b/YaeBlog/source/posts/parser-combinator/image-20240813214315576.webp
    new file mode 100644
    index 0000000..a549db4
    --- /dev/null
    +++ b/YaeBlog/source/posts/parser-combinator/image-20240813214315576.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:92c8739d775414fe9e06c338bc2635529da4e2066ee76273cce42cc0d67020bf
    +size 13462
    diff --git a/YaeBlog/source/posts/parser-combinator/image-20240813220521028.png b/YaeBlog/source/posts/parser-combinator/image-20240813220521028.png
    deleted file mode 100644
    index dc4ba0b..0000000
    --- a/YaeBlog/source/posts/parser-combinator/image-20240813220521028.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:608d183ed3511d9c42ad2cd79f576c42609c99fb94a3014f8c841d5d95bf6a6d
    -size 164153
    diff --git a/YaeBlog/source/posts/parser-combinator/image-20240813220521028.webp b/YaeBlog/source/posts/parser-combinator/image-20240813220521028.webp
    new file mode 100644
    index 0000000..68bc9cf
    --- /dev/null
    +++ b/YaeBlog/source/posts/parser-combinator/image-20240813220521028.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:fefce83495bdc3ae267b93ec2e46e7979fe528329c69156b61e2956abdbd216e
    +size 60562
    diff --git a/YaeBlog/source/posts/parser-combinator/image-20240813220530717.png b/YaeBlog/source/posts/parser-combinator/image-20240813220530717.png
    deleted file mode 100644
    index 7f01189..0000000
    --- a/YaeBlog/source/posts/parser-combinator/image-20240813220530717.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:3db9d6b10173fd0bfa8dab9ae7c52b4f4ac151d2f2322cf34c055029e1560e69
    -size 106064
    diff --git a/YaeBlog/source/posts/parser-combinator/image-20240813220530717.webp b/YaeBlog/source/posts/parser-combinator/image-20240813220530717.webp
    new file mode 100644
    index 0000000..a60dbb8
    --- /dev/null
    +++ b/YaeBlog/source/posts/parser-combinator/image-20240813220530717.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:106855c0f051d0130474330fe5944752bb6236dea870584e9152ac09efc8352e
    +size 53078
    diff --git a/YaeBlog/source/posts/program-design-introduction.md b/YaeBlog/source/posts/program-design-introduction.md
    index 1171fdd..7dabd70 100644
    --- a/YaeBlog/source/posts/program-design-introduction.md
    +++ b/YaeBlog/source/posts/program-design-introduction.md
    @@ -1,18 +1,19 @@
     ---
     title: 程序设计与计算导论笔记
    -date: 2021-11-17 16:38:30
    +date: 2021-11-17T16:38:30.0000000
     tags:
    -    - 学习资料
    +- 学习资料
     ---
     
    +
     直接扒的老黄的PPT ,简直毫无参考价值。   
     不保证笔记的绝对正确性。     
     如果导致考试爆炸,不承担任何责任。
          
     
     ## 第一章——程序设计概论
    -![第一章](./program-design-introduction/1.png)
    +![第一章](./program-design-introduction/1.webp)
     
     ## 第二章——C语言概述
    -![第二章](./program-design-introduction/2.png)
    -![第二章](./program-design-introduction/3.png)
    +![第二章](./program-design-introduction/2.webp)
    +![第二章](./program-design-introduction/3.webp)
    diff --git a/YaeBlog/source/posts/program-design-introduction/1.png b/YaeBlog/source/posts/program-design-introduction/1.png
    deleted file mode 100644
    index 6253516..0000000
    --- a/YaeBlog/source/posts/program-design-introduction/1.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:6c1ff62a089d129cd7f1d62977829d2625627a758725aa5171af9bbddb61e1b0
    -size 263019
    diff --git a/YaeBlog/source/posts/program-design-introduction/1.webp b/YaeBlog/source/posts/program-design-introduction/1.webp
    new file mode 100644
    index 0000000..689fc76
    --- /dev/null
    +++ b/YaeBlog/source/posts/program-design-introduction/1.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:d57d00ba1d8fb8ba0d8e5fa0016eeaac4e270f927b1bcfcc2a2c9ac464fba53f
    +size 359910
    diff --git a/YaeBlog/source/posts/program-design-introduction/2.png b/YaeBlog/source/posts/program-design-introduction/2.png
    deleted file mode 100644
    index 240ecbf..0000000
    --- a/YaeBlog/source/posts/program-design-introduction/2.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:700eff5428a635793910f8058dd83c395f661f68497c0a5e7f780b03cc5c9081
    -size 315995
    diff --git a/YaeBlog/source/posts/program-design-introduction/2.webp b/YaeBlog/source/posts/program-design-introduction/2.webp
    new file mode 100644
    index 0000000..f4bef91
    --- /dev/null
    +++ b/YaeBlog/source/posts/program-design-introduction/2.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:5a4836148c40f2ba7129eadbb48fd372e2fd316c343157fe08c41f55113d3840
    +size 420884
    diff --git a/YaeBlog/source/posts/program-design-introduction/3.png b/YaeBlog/source/posts/program-design-introduction/3.png
    deleted file mode 100644
    index 64b21dc..0000000
    --- a/YaeBlog/source/posts/program-design-introduction/3.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:3e4a7ac08ebc2f1c524b9c0b1eefc97d5f9a855251f36cab44be81d901b1ea1f
    -size 686513
    diff --git a/YaeBlog/source/posts/program-design-introduction/3.webp b/YaeBlog/source/posts/program-design-introduction/3.webp
    new file mode 100644
    index 0000000..c5f86f3
    --- /dev/null
    +++ b/YaeBlog/source/posts/program-design-introduction/3.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:84d3936b5beb8b410df70a7e4e169451c859305ef97bc2214a5433fb01f2db59
    +size 922942
    diff --git a/YaeBlog/source/posts/qt-learning.md b/YaeBlog/source/posts/qt-learning.md
    index c18cf5e..15e380b 100644
    --- a/YaeBlog/source/posts/qt-learning.md
    +++ b/YaeBlog/source/posts/qt-learning.md
    @@ -1,12 +1,12 @@
     ---
     title: 初学Qt的一点小笔记
    +date: 2022-07-01T14:32:39.0000000
     tags:
    -  - 技术笔记
    -  - C/C++
    -typora-root-url: qt-learning
    -date: 2022-07-01 14:32:39
    +- 技术笔记
    +- C/C++
     ---
     
    +
     最近的大作业需要用 `C/C++`的技术栈实现一个图形化界面,`Qt`作为C++图形化框架久负盛名,正好借着这个写大作业的机会学习一下这个应用广泛的框架。
     
     
    @@ -45,7 +45,7 @@ errorMsg.obj : error LNK2001: unresolved external symbol "public: virtual int __
     - 在`CMakeLists.txt`文件中需要定义`set(CMAKE_AUTOMOC ON)`
     - 在包含这个头文件的地方需要将头文件的名称改为预处理之后的名称`moc_*.cpp`,如下图所示
     
    -![](1.png)
    +![](1.webp)
     
     ## Qt Designer
     
    diff --git a/YaeBlog/source/posts/qt-learning/1.png b/YaeBlog/source/posts/qt-learning/1.png
    deleted file mode 100644
    index e5e118a..0000000
    --- a/YaeBlog/source/posts/qt-learning/1.png
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -version https://git-lfs.github.com/spec/v1
    -oid sha256:44d4b15d38af85412c9a7e502a502afaa9bac3c08b91db89207edfe90ee9ea1f
    -size 52016
    diff --git a/YaeBlog/source/posts/qt-learning/1.webp b/YaeBlog/source/posts/qt-learning/1.webp
    new file mode 100644
    index 0000000..0fe27ab
    --- /dev/null
    +++ b/YaeBlog/source/posts/qt-learning/1.webp
    @@ -0,0 +1,3 @@
    +version https://git-lfs.github.com/spec/v1
    +oid sha256:0ff8fa0f8cbfb3de6bfb98fb3d18ae4f5c9798c109d95ee14a5482755445ddd1
    +size 27244
    diff --git a/YaeBlog/source/posts/question-in-install-vs-2019.md b/YaeBlog/source/posts/question-in-install-vs-2019.md
    index 4abbaf6..c11adcc 100644
    --- a/YaeBlog/source/posts/question-in-install-vs-2019.md
    +++ b/YaeBlog/source/posts/question-in-install-vs-2019.md
    @@ -1,10 +1,11 @@
     ---
     title: 安装vs2019踩的坑
    -date: 2021-08-14 10:06:00
    +date: 2021-08-14T10:06:00.0000000
     tags:
    -    - 技术笔记
    +- 技术笔记
     ---
     
    +
     在某个月黑风高的夜晚,我在折腾了很久的Python之后,突然感觉自己应该去学学C和C++,于是乎我便打算折腾一下在vscode上写C和C++。在网上一番搜寻之后,我发现了这篇[知乎文章](https://zhuanlan.zhihu.com/p/87864677)和这篇[知乎文章](https://zhuanlan.zhihu.com/p/147366852),然后我就被安装MinGW编译器和配置一大堆的json文件给干碎了。
    于是,我决定转向传说中的宇宙第一IDE——Visual Studio。
    @@ -12,7 +13,7 @@ tags: > 无他,就是太穷了。
    在点击下载链接之后,跳转到了感谢下载的界面。
    -![](./question-in-install-vs-2019/1.png) +![](./question-in-install-vs-2019/1.webp) 上面清楚的写着,下载将很快开始。 在经过漫长的等待之后,我下载下来了这个东西 >vs_community__355502915.1625217430.exe @@ -24,7 +25,7 @@ tags: >不可能,微软的软件不会出错,一定是我自己选错了。 我立刻卸载了电脑上的vs2017和Visual Studio Installer,再次前往vs的[官网](https://visualstudio.microsoft.com/zh-hans/vs/),这次我前检查后检查,确定自己下载的是Visual Studio 2019的Community版本,在单击了下载链接之后,我又跳转到了感谢下载的界面。 -![](./question-in-install-vs-2019/1.png) +![](./question-in-install-vs-2019/1.webp) 但是这次,在令人十分无语的等待之后,我不得不点了下单击此处以重试。 在一段令人紧张的等待之后,Visual Studio Installer安装好了,我也失望的发现,我安装的仍然是vs2017。 >谢谢,电脑已经砸了。 diff --git a/YaeBlog/source/posts/question-in-install-vs-2019/1.png b/YaeBlog/source/posts/question-in-install-vs-2019/1.png deleted file mode 100644 index 97c6d07..0000000 --- a/YaeBlog/source/posts/question-in-install-vs-2019/1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b6fd368bfe4a7defb24d72f069ca7a7bf8f69a417ac65e8dd7c526576cc4f14e -size 229076 diff --git a/YaeBlog/source/posts/question-in-install-vs-2019/1.webp b/YaeBlog/source/posts/question-in-install-vs-2019/1.webp new file mode 100644 index 0000000..ccea748 --- /dev/null +++ b/YaeBlog/source/posts/question-in-install-vs-2019/1.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8541c37d3a7edfe50f6e8c4cb0d2ef8a5b860a9ae95e2d2adcfaa3326463f3ec +size 96866 diff --git a/YaeBlog/source/posts/rust-drop-stack-overflow.md b/YaeBlog/source/posts/rust-drop-stack-overflow.md index 37927aa..abfded7 100644 --- a/YaeBlog/source/posts/rust-drop-stack-overflow.md +++ b/YaeBlog/source/posts/rust-drop-stack-overflow.md @@ -6,6 +6,7 @@ tags: - 技术笔记 --- + 这辈子就是被Rust编译器害了.jpg @@ -14,15 +15,15 @@ tags: 事情是这样的,当我实现完`Sysy`语言的语法分析器并编写了一些白盒测试用例之后,我便打算将官方提供的100个测试用例作为输入运行看看能不能**正常**的解析成抽象语法树(显然不可能手动检查生成的抽象语法树是否正确)。我首先在`main.rs`里面实现了读取所有的`.sy`文件,进行词法分析和语法分析的逻辑,程序在这里这正常的识别了大多数的输入文件,在一些浮点数的输入上还存在一些问题。于是我便打算将这些逻辑重构到一个Rust的集成测试中,方便在CI中使用`cargo test`进行运行测试。但是在重构完成之后使用`cargo test`进行运行时我去遇到了如下的运行时错误。 -![image-20241105181144993](./rust-drop-stack-overflow/image-20241105181144993.png) +![image-20241105181144993](./rust-drop-stack-overflow/image-20241105181144993.webp) 看到这个报错的第一瞬间,我怀疑是因为`cargo test`和`cargo run`的运行环境不同,导致测试程序读取到了其他其实不是`sysy`程序但是以`.sy`结尾的文件,而恰好这个文件又能被解析,使得解析器组合子工作的过程中调用链太长而导致栈溢出,于是我在`RustRover`中打断点调试运行,却发现程序正确的读取到输入文件。这就奇怪了,我于是让程序继续运行到报错,看看报错时候程序的调用栈是被什么东西填满了,然后发现程序的调用栈长这样: -![image-20241105181612954](./rust-drop-stack-overflow/image-20241105181612954.png) +![image-20241105181612954](./rust-drop-stack-overflow/image-20241105181612954.webp) 并不是我程序中代码的调用太深导致的,而是Rust编译器自动生成的`drop`函数导致的。于是尝试看看调用栈的底部,看看是在读取什么输入数据,`drop`什么神仙数据结构的时候发生的。调试器很快告诉我们,`drop`的数据结构是抽象语法树中的二元表达式,而此时的输入代码则如下图所示,而且图中的代码重复了400行。 -![image-20241105182036975](./rust-drop-stack-overflow/image-20241105182036975.png) +![image-20241105182036975](./rust-drop-stack-overflow/image-20241105182036975.webp) 我已经能想象到那棵高耸如云的抽象语法树了。 diff --git a/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181144993.png b/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181144993.png deleted file mode 100644 index 93f1df8..0000000 --- a/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181144993.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8cfa995fa0114dc3ca2a412da668d1e35e28072f06bb44b5abc8759c15d71275 -size 49002 diff --git a/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181144993.webp b/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181144993.webp new file mode 100644 index 0000000..c73fb82 --- /dev/null +++ b/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181144993.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47978418a849a3b60591de273a4041d739ef69de949af21d6a395151c7cfdff8 +size 12372 diff --git a/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181612954.png b/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181612954.png deleted file mode 100644 index 6c6699d..0000000 --- a/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181612954.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eba69761c69d220e829c43d064ba652b2bd5f7bf3a1f93c37c22a5e332b284a9 -size 81175 diff --git a/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181612954.webp b/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181612954.webp new file mode 100644 index 0000000..aea52eb --- /dev/null +++ b/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105181612954.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f7c19288ab5cd8430c88796535dc598b5650939c3aab306c1f0b93cac716c99 +size 24398 diff --git a/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105182036975.png b/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105182036975.png deleted file mode 100644 index 6c1317e..0000000 --- a/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105182036975.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4797186c7079795d37728da7227101aac6c8ce8b0da11af988f7ea5606191364 -size 43027 diff --git a/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105182036975.webp b/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105182036975.webp new file mode 100644 index 0000000..4f45fda --- /dev/null +++ b/YaeBlog/source/posts/rust-drop-stack-overflow/image-20241105182036975.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a46c711a6735d5581ff208b4fa2943622bb9ea1bfb9e8bd06f52b27e321cf479 +size 8022 diff --git a/YaeBlog/source/posts/software-engineer.md b/YaeBlog/source/posts/software-engineer.md index a5b0229..ddcd614 100644 --- a/YaeBlog/source/posts/software-engineer.md +++ b/YaeBlog/source/posts/software-engineer.md @@ -1,11 +1,12 @@ --- title: 软件工程 +date: 2024-06-12T20:27:25.0000000 tags: - - 软件工程 - - 学习资料 -date: 2024-06-12 20:27:25 +- 软件工程 +- 学习资料 --- + 你说得对,但软工无非就是瀑布模型规定的那六项基本活动及其变化,然后细化到需求分析需要用UML规范化表示的建模方法,后面就是如何通过框架模块的结构确定软件对象有哪些并分配功能。当然这其中也涉及到模块独立性的设计原则以及面向对象的7个设计原则,之后就是软件测试的白盒和黑盒方法。 ## 软件工程概述 @@ -61,7 +62,7 @@ F.L. Bauer提出,软件工程就是为了经济地获得能够在实际机器 #### 瀑布模型 -![image-20240620211321957](software-engineer/image-20240620211321957.png) +![image-20240620211321957](software-engineer/image-20240620211321957.webp) 瀑布模型强调在软件实现之前必须进行分析和设计工作,同时也造成了模型缺乏灵活性的问题,特别是面对软件的需求不明确或者不准确的情况时,模型的风险控制能力较弱。 @@ -71,19 +72,19 @@ F.L. Bauer提出,软件工程就是为了经济地获得能够在实际机器 烟火模型提倡进行两次开发。第一次开发得到实验性的原型产品,其目的是探索可行性,弄清软件的需求。第二次在第一次的基础上获得较为满意的软件产品。 -![image-20240620212101864](software-engineer/image-20240620212101864.png) +![image-20240620212101864](software-engineer/image-20240620212101864.webp) 演化模型在明确用户需求和降低开发风险的同时,也带来了难以管理、结构较差和技术不成熟的问题,同时可能抛弃掉瀑布模型的文档控制优点。因此演化模型更多适合于需求不清晰的小型或中型系统,适用于开发周期较短的场合。 #### 增量模型 -![image-20240620214307906](software-engineer/image-20240620214307906.png) +![image-20240620214307906](software-engineer/image-20240620214307906.webp) 但是在增量模型中难以确定增量的粒度,同时确定所有的需求比较困难。 #### 喷泉模型 -![image-20240620214739548](software-engineer/image-20240620214739548.png) +![image-20240620214739548](software-engineer/image-20240620214739548.webp) 各个开发阶段没有特定的次序,可以并行进行,也可以在某个开发阶段中随时补充其他任何开发阶段。但是难以管理,工作计划需要随时更新。 @@ -91,11 +92,11 @@ F.L. Bauer提出,软件工程就是为了经济地获得能够在实际机器 将测试活动提前,使得瀑布模型能够驾驭风险。 -![image-20240620215022645](software-engineer/image-20240620215022645.png) +![image-20240620215022645](software-engineer/image-20240620215022645.webp) #### 螺旋模型 -![image-20240620220155962](software-engineer/image-20240620220155962.png) +![image-20240620220155962](software-engineer/image-20240620220155962.webp) 四个象限:制定计划、风险分析、实施工程、客户评价。 @@ -103,13 +104,13 @@ F.L. Bauer提出,软件工程就是为了经济地获得能够在实际机器 利用模块化思想将整个系统模块化,并在一定构件模型的支持下复用构件库中软件构件,通过组装高效率、高质量地构造软件系统。构件组装模型本质上是演化模型。 -![image-20240620224318982](software-engineer/image-20240620224318982.png) +![image-20240620224318982](software-engineer/image-20240620224318982.webp) 充分利用软件复用,提高开发效率。允许多个项目同时开发,降低了费用,提高了可维护性。但是缺乏通用的构件组装结构标准,风险较大,同时构件可重用性和系统高效性之间不易协调。 #### 快速应用开发模型 -![image-20240620224519243](software-engineer/image-20240620224519243.png) +![image-20240620224519243](software-engineer/image-20240620224519243.webp) 增量型的软件开发过程,强调极短的开发周期。 @@ -142,7 +143,7 @@ RUP由9个核心工作流构建每个迭代的主要活动。分别是6个核心 敏捷建模由一系列的价值观、原则和实践组成,是一种快速软件开发的思想代码,代表性的应用有极限编程。 -![image-20240621114540033](software-engineer/image-20240621114540033.png) +![image-20240621114540033](software-engineer/image-20240621114540033.webp) 极限编程中的需求分析要求开发人员和客户一起将各种需求变成小的需求模块(User Story),这些模块将会被组合或者分解为更小的模块,成为Story Card。 @@ -179,13 +180,13 @@ UML,即统一建模语言,是面向对象分析与设计的一种标准表 4+1视图,通过模型来描述系统的结构和行为,或者说静态结构和动态结构,以用例视图为核心描述系统的不同视图。 -![image-20240623160826903](software-engineer/image-20240623160826903.png) +![image-20240623160826903](software-engineer/image-20240623160826903.webp) #### 领域模型 领域模型是针对某一个特定领域内概念类或者对象的抽象化可视化表示。 -![image-20240623162229404](software-engineer/image-20240623162229404.png) +![image-20240623162229404](software-engineer/image-20240623162229404.webp) 在领域模型中使用类图表达概念及其关系,概念类之间的关系有关联、继承、组合、聚合和依赖等关系。 @@ -203,11 +204,11 @@ UML,即统一建模语言,是面向对象分析与设计的一种标准表 模块的内聚性是模块功能强度的度量。 -![image-20240623163647935](software-engineer/image-20240623163647935.png) +![image-20240623163647935](software-engineer/image-20240623163647935.webp) 模块的耦合性是针对模块之间连接紧密程度的度量。 -![image-20240623170058679](software-engineer/image-20240623170058679.png) +![image-20240623170058679](software-engineer/image-20240623170058679.webp) 面向对象的设计原则: diff --git a/YaeBlog/source/posts/software-engineer/image-20240620211321957.png b/YaeBlog/source/posts/software-engineer/image-20240620211321957.png deleted file mode 100644 index d5e6f36..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240620211321957.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9feaffe8bb9531f3d7dcd49b836fe2615f43a32c1833a3e95f17ffd6c109db31 -size 134202 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620211321957.webp b/YaeBlog/source/posts/software-engineer/image-20240620211321957.webp new file mode 100644 index 0000000..87d088e --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240620211321957.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b2697e6eb8393a0d6fc9a7b7a0c5a8b6fcf1eaa41bff77a797d76b966159fc5 +size 21830 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620212101864.png b/YaeBlog/source/posts/software-engineer/image-20240620212101864.png deleted file mode 100644 index adae8d9..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240620212101864.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:682ca3ab11829c8071189772060837063cbb62b5fcfde848aa71a15280cfa091 -size 190854 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620212101864.webp b/YaeBlog/source/posts/software-engineer/image-20240620212101864.webp new file mode 100644 index 0000000..38bd965 --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240620212101864.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97fa916111246230e28cfc64fcf6fbba2afc2e00924198f2fda575d37a84fda2 +size 30260 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620214307906.png b/YaeBlog/source/posts/software-engineer/image-20240620214307906.png deleted file mode 100644 index 626f115..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240620214307906.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a244a26879b14d0dc35daf6218305d2fd96a822c77999bf36404904e6a9e394a -size 273406 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620214307906.webp b/YaeBlog/source/posts/software-engineer/image-20240620214307906.webp new file mode 100644 index 0000000..40b2249 --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240620214307906.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a4bbaf9c565fde3742a8dd345596ff6abe38fa116233cd3fc96702e48f9f858 +size 44954 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620214739548.png b/YaeBlog/source/posts/software-engineer/image-20240620214739548.png deleted file mode 100644 index 50c2977..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240620214739548.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2fbce72d6f07c49345b1613b1ac4d9d0d7546fa8c5688c4dff923d115e5788ce -size 108027 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620214739548.webp b/YaeBlog/source/posts/software-engineer/image-20240620214739548.webp new file mode 100644 index 0000000..a859714 --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240620214739548.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5297367eab21bfd75cfa6344fa0f5f7d0dcc8bef5223216ab9542020f5a01af3 +size 50498 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620215022645.png b/YaeBlog/source/posts/software-engineer/image-20240620215022645.png deleted file mode 100644 index 8f1cec5..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240620215022645.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:feb20813948235d1dca857baf94dbf1212729b18db289212b55cacf9ae92dc74 -size 113775 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620215022645.webp b/YaeBlog/source/posts/software-engineer/image-20240620215022645.webp new file mode 100644 index 0000000..a75f89e --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240620215022645.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d46328f2e4f3e91dbcaaa880f7b02d847254668a481c301ea4ba04718d12b88 +size 63100 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620220155962.png b/YaeBlog/source/posts/software-engineer/image-20240620220155962.png deleted file mode 100644 index cef4bbe..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240620220155962.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0b35bb450ffbe93bfd8d3d752fcce55b1bf7116f220e1eee5ba275097d67ce30 -size 194548 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620220155962.webp b/YaeBlog/source/posts/software-engineer/image-20240620220155962.webp new file mode 100644 index 0000000..93f9a1b --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240620220155962.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e232c96e9826a39a2123d6048de17b6d3c1d911f63c03c7618c5454e0621588e +size 51084 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620224318982.png b/YaeBlog/source/posts/software-engineer/image-20240620224318982.png deleted file mode 100644 index 1f4f278..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240620224318982.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:faf61882914e3fab1b02847bedfc5482561e871644693d630efdc8b783b5e7d4 -size 119180 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620224318982.webp b/YaeBlog/source/posts/software-engineer/image-20240620224318982.webp new file mode 100644 index 0000000..dcb50de --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240620224318982.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84698ab4aacdb986e6f74a30f95e894e041821f40401f77ce67b11a57528bcbf +size 63542 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620224519243.png b/YaeBlog/source/posts/software-engineer/image-20240620224519243.png deleted file mode 100644 index a898b5d..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240620224519243.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e4334f70d148d512df0abea0eadc553a1f7d7dc9a923faac6fdb6ad581f63618 -size 190877 diff --git a/YaeBlog/source/posts/software-engineer/image-20240620224519243.webp b/YaeBlog/source/posts/software-engineer/image-20240620224519243.webp new file mode 100644 index 0000000..70d5e47 --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240620224519243.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5d2cc95f6aea29c53f517a968a4a21f69cd10c9b8e2a350ee6d3bb4a21a1754 +size 26200 diff --git a/YaeBlog/source/posts/software-engineer/image-20240621114540033.png b/YaeBlog/source/posts/software-engineer/image-20240621114540033.png deleted file mode 100644 index ec7791d..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240621114540033.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a8ae17c7517b83ed4429ce148ebfd60595ea94127405117af34bec316abe4956 -size 172404 diff --git a/YaeBlog/source/posts/software-engineer/image-20240621114540033.webp b/YaeBlog/source/posts/software-engineer/image-20240621114540033.webp new file mode 100644 index 0000000..d3e1976 --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240621114540033.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6711902df854e2b52b811a2669e5d1257d4dec0b05e9fcacff98ea15f8b0cc62 +size 37034 diff --git a/YaeBlog/source/posts/software-engineer/image-20240623160826903.png b/YaeBlog/source/posts/software-engineer/image-20240623160826903.png deleted file mode 100644 index cb255b4..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240623160826903.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4c375a4e5fe9910ee9263a1878c43c557e92cbf4d88a35af01e7b5d2e2d1d010 -size 155772 diff --git a/YaeBlog/source/posts/software-engineer/image-20240623160826903.webp b/YaeBlog/source/posts/software-engineer/image-20240623160826903.webp new file mode 100644 index 0000000..3f0ba5e --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240623160826903.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:719075736d61689f9a62b5fc4726ab1a5fed84c95513a7946745b7fbce7b001f +size 25908 diff --git a/YaeBlog/source/posts/software-engineer/image-20240623162229404.png b/YaeBlog/source/posts/software-engineer/image-20240623162229404.png deleted file mode 100644 index 74c7155..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240623162229404.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:87403c9ee1023bf2442d7aa095083f12136d56872733dbe565202487293bea2a -size 330278 diff --git a/YaeBlog/source/posts/software-engineer/image-20240623162229404.webp b/YaeBlog/source/posts/software-engineer/image-20240623162229404.webp new file mode 100644 index 0000000..3b1b77f --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240623162229404.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a6204468dafff77a9498f44d972cd743b109aa4c62618325b83c8bd94714eb6 +size 40468 diff --git a/YaeBlog/source/posts/software-engineer/image-20240623163647935.png b/YaeBlog/source/posts/software-engineer/image-20240623163647935.png deleted file mode 100644 index f1c700d..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240623163647935.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:51743c989c50d02e37c103a243e1afc6d922f8f231feec802cdd400f331598e1 -size 146135 diff --git a/YaeBlog/source/posts/software-engineer/image-20240623163647935.webp b/YaeBlog/source/posts/software-engineer/image-20240623163647935.webp new file mode 100644 index 0000000..23be7f8 --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240623163647935.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e3cf05b12b962dd28b0c2cf4d400d7f9b570eb5221477d0760c57d797986e5b +size 25066 diff --git a/YaeBlog/source/posts/software-engineer/image-20240623170058679.png b/YaeBlog/source/posts/software-engineer/image-20240623170058679.png deleted file mode 100644 index 2b84b34..0000000 --- a/YaeBlog/source/posts/software-engineer/image-20240623170058679.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2a6b024c357a128ade7ebc5f6a853c236942c05ad9461b3ca7b781dc88b9158a -size 115178 diff --git a/YaeBlog/source/posts/software-engineer/image-20240623170058679.webp b/YaeBlog/source/posts/software-engineer/image-20240623170058679.webp new file mode 100644 index 0000000..b774fc4 --- /dev/null +++ b/YaeBlog/source/posts/software-engineer/image-20240623170058679.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0602498704ee426ed4cc6458e3812ba19adde20ecaac9c5e0ce6cb757fb16a2c +size 58316 diff --git a/YaeBlog/source/posts/spring-boot-custom-authorize.md b/YaeBlog/source/posts/spring-boot-custom-authorize.md index 85736d1..c4d1a63 100644 --- a/YaeBlog/source/posts/spring-boot-custom-authorize.md +++ b/YaeBlog/source/posts/spring-boot-custom-authorize.md @@ -1,11 +1,12 @@ --- title: SpringBoot自定义注解实现权限控制 +date: 2023-07-29T15:20:02.0000000 tags: - - 技术笔记 - - Java -date: 2023-07-29 15:20:02 +- 技术笔记 +- Java --- + 如题。 @@ -131,13 +132,13 @@ public interface AuthorizeService { 然后针对每个策略实现一个认证服务: -![image-20230727175807814](spring-boot-custom-authorize/image-20230727175807814.png) +![image-20230727175807814](spring-boot-custom-authorize/image-20230727175807814.webp) > 具体实现就不在这里给出。 在注入每个服务时,使用策略枚举作为服务的名称,方便后续获得该服务实例。 -![image-20230727175955817](spring-boot-custom-authorize/image-20230727175955817.png) +![image-20230727175955817](spring-boot-custom-authorize/image-20230727175955817.webp) ### 注解和注解解析 diff --git a/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175807814.png b/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175807814.png deleted file mode 100644 index 29256a0..0000000 --- a/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175807814.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:104460bc9297f7d1eb89d4bf1dc2d516bd371183c0169427af4ba47a9ac069d5 -size 34903 diff --git a/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175807814.webp b/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175807814.webp new file mode 100644 index 0000000..7b5cec1 --- /dev/null +++ b/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175807814.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eea0b3896b8d71c1f795b193cf262851e2d5e1930ba977b5bec070dc1e3f5146 +size 6198 diff --git a/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175955817.png b/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175955817.png deleted file mode 100644 index 67320a9..0000000 --- a/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175955817.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a5878ef72ef9f57be94a7a7fe5a02297131d87ae94b61c3a593d9d3311fff1ae -size 27551 diff --git a/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175955817.webp b/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175955817.webp new file mode 100644 index 0000000..45f3263 --- /dev/null +++ b/YaeBlog/source/posts/spring-boot-custom-authorize/image-20230727175955817.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1953b390215fa766bd500b202b403ac9e20578c7f85911055d207ea06aa424b +size 17224 diff --git a/YaeBlog/source/posts/vscode-in-browser.md b/YaeBlog/source/posts/vscode-in-browser.md index a24b8af..842578c 100644 --- a/YaeBlog/source/posts/vscode-in-browser.md +++ b/YaeBlog/source/posts/vscode-in-browser.md @@ -1,10 +1,11 @@ --- title: 在浏览器中打开VSCode -date: 2021-10-30 17:16:13 +date: 2021-10-30T17:16:13.0000000 tags: - - 技术笔记 +- 技术笔记 --- + 众所周知,VSCode作为大微软家开发的开源编辑器,一经发布便受到了两广大程序员群体的欢迎。如果我们深入的了解一下VSCode,就会知道VSCode是基于Electron框架构建的Web应用程序,而Electron框架是基于Web技术来开发桌面应用程序,即VSCode只要稍加改造,就可以流畅的在浏览器中运行。那么我们如何才能在浏览器打开一个VSCode呢? 最简单的方法是在浏览器中输入[这个网址](https://vscode.dev),就可以在浏览器中打开一个VSCode Online,这个版本的VSCode可以支持打开本地的文件,并且进行编辑。不过这个编辑器并不支持大部分的插件,而且并不支持程序的编译与运行,并不是一个可以开箱即用的编辑器。那么还有什么办法可以让我们拥有一个联网即可得的个人定制化开发环境呢? @@ -53,9 +54,9 @@ cert: false ``` 在第一行,127.0.0.1代表这是本机的IP,如果要在公网上访问的话,需要将这里的IP改为0.0.0.0,后面的端口在默认的情况下是8080,你可以改成自己喜欢的端口号,在第二行的password表示采用密码进行身份验证,我们需要在第三行设置自己熟悉的密码,以方便自己的访问,当然,你把默认生成的密码背下来应该也是可以的 在进行了这些更改之后,我们再次输入code-server重启服务,如果一次顺利,我们可以看见以下的启动信息 -![启动信息](./vscode-in-browser/1.png) +![启动信息](./vscode-in-browser/1.webp) 我们可以打开浏览器,在地址栏中输入你的服务器公网IP加上你自己设置的端口号,就可以打开自己的VSCode Online界面了。 -![主界面](./vscode-in-browser/2.png) +![主界面](./vscode-in-browser/2.webp) 输入自己的设置密码,就可以开始把浏览器中的VSCode当作自己本地计算机上的VSCode使用了,不过其中的文件是位于自己的服务器上的。 >如果你和我一样使用的阿里云的服务器,可能还需要到服务器的管理界面设置安全组放行相应的端口,具体参考[这篇文章](https://help.aliyun.com/document_detail/59086.html?spm=5176.10173289.help.dexternal.4ff02e77892BZP) diff --git a/YaeBlog/source/posts/vscode-in-browser/1.png b/YaeBlog/source/posts/vscode-in-browser/1.png deleted file mode 100644 index 0523623..0000000 --- a/YaeBlog/source/posts/vscode-in-browser/1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a74cd47bed9e46670d2b730a21fe550ea17609b552567f2a3ef8bbfc6db5a253 -size 37385 diff --git a/YaeBlog/source/posts/vscode-in-browser/1.webp b/YaeBlog/source/posts/vscode-in-browser/1.webp new file mode 100644 index 0000000..630199c --- /dev/null +++ b/YaeBlog/source/posts/vscode-in-browser/1.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25d3c15f13764c382ec4bcaf3cbb4babec22de0ca7f8681c5e3065a7a358cf8a +size 7062 diff --git a/YaeBlog/source/posts/vscode-in-browser/2.png b/YaeBlog/source/posts/vscode-in-browser/2.png deleted file mode 100644 index c18e033..0000000 --- a/YaeBlog/source/posts/vscode-in-browser/2.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d9d048021f921128b5daef4b4b884cb40d54367c6bb2840f0ddec722946b2824 -size 53525 diff --git a/YaeBlog/source/posts/vscode-in-browser/2.webp b/YaeBlog/source/posts/vscode-in-browser/2.webp new file mode 100644 index 0000000..8a699d1 --- /dev/null +++ b/YaeBlog/source/posts/vscode-in-browser/2.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26dd469996330f22d0c1bf26224fe6781d5ce0f05dea8d48ec28f3cc46563d0c +size 20430 diff --git a/YaeBlog/source/posts/whats-new-of-dotnet-8.md b/YaeBlog/source/posts/whats-new-of-dotnet-8.md index d551565..2fe519e 100644 --- a/YaeBlog/source/posts/whats-new-of-dotnet-8.md +++ b/YaeBlog/source/posts/whats-new-of-dotnet-8.md @@ -1,12 +1,13 @@ --- title: .Net 8中我关心的新功能 -date: 2023-11-22 21:07:03 +date: 2023-11-22T21:07:03.0000000 tags: - - 技术笔记 - - dotnet +- 技术笔记 +- dotnet --- + ## .NET 首先看看.NET 8的新增功能,参照官方文档中的[.NET 8的新增功能](https://learn.microsoft.com/zh-cn/dotnet/core/whats-new/dotnet-8)。 @@ -307,11 +308,11 @@ ab -n 10000 -c 1000 http://locallhost:5000/todos/ 测试之后的结果的如图所示: -![image-20231122100930849](./whats-new-of-dotnet-8/image-20231122100930849.png) +![image-20231122100930849](./whats-new-of-dotnet-8/image-20231122100930849.webp) 直接使用运行时运行编译出来的`dll`文件: -![image-20231122101012416](./whats-new-of-dotnet-8/image-20231122101012416.png) +![image-20231122101012416](./whats-new-of-dotnet-8/image-20231122101012416.webp) 修改项目文件和源代码,取消`System.Text.Json`使用源生成器和对于`AOT`的支持。 @@ -346,11 +347,11 @@ public record Todo(int Id, string? Title, DateOnly? DueBy = null, bool IsComplet 使用同样的命令测试得到的结果如图: -![image-20231122101142080](./whats-new-of-dotnet-8/image-20231122101142080.png) +![image-20231122101142080](./whats-new-of-dotnet-8/image-20231122101142080.webp) 可以发现几次测试的结果都比较接近,几乎可以认为`AOT`编译不会对性能产生明显的影响。虽然按照官方博客中给出的图片,`AOT`编译是有性能下降,应该是我这边测试工具的瓶颈。 -![Before and After AOT](./whats-new-of-dotnet-8/AOTOptimizations4.png) +![Before and After AOT](./whats-new-of-dotnet-8/AOTOptimizations4.webp) ### Identity API 终结点 diff --git a/YaeBlog/source/posts/whats-new-of-dotnet-8/AOTOptimizations4.png b/YaeBlog/source/posts/whats-new-of-dotnet-8/AOTOptimizations4.png deleted file mode 100644 index 713c7ac..0000000 --- a/YaeBlog/source/posts/whats-new-of-dotnet-8/AOTOptimizations4.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8c307a43534321f2e35bf61d5ee5c29fe571e3f2dc9ae23c8d3f5ebf11e350e2 -size 312444 diff --git a/YaeBlog/source/posts/whats-new-of-dotnet-8/AOTOptimizations4.webp b/YaeBlog/source/posts/whats-new-of-dotnet-8/AOTOptimizations4.webp new file mode 100644 index 0000000..b46ddc9 --- /dev/null +++ b/YaeBlog/source/posts/whats-new-of-dotnet-8/AOTOptimizations4.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b440e741b2aa0a0d302a5137898cf909fe0999840e6776403d0ec71aec6a9a8 +size 74832 diff --git a/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122100930849.png b/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122100930849.png deleted file mode 100644 index e527a83..0000000 --- a/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122100930849.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6ad8ef505acf3d492b886658924ab6db8cf889ba5a43207c89f1bae77b52501b -size 76115 diff --git a/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122100930849.webp b/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122100930849.webp new file mode 100644 index 0000000..caaca4c --- /dev/null +++ b/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122100930849.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59b34d52384b2bdfd7b0e46a6d32cca1256eec56dbf3968eb9d35b174748bfb6 +size 13238 diff --git a/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101012416.png b/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101012416.png deleted file mode 100644 index d937496..0000000 --- a/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101012416.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e67ea7e7349e2805c6ef74c64a4121bd06ec50875ece402827ff40f640f2d2de -size 75303 diff --git a/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101012416.webp b/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101012416.webp new file mode 100644 index 0000000..04c452f --- /dev/null +++ b/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101012416.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40a5125c6bb0860ee004c404d03d843865020fbdbcf9bdcbc8aec743f7eb91be +size 12984 diff --git a/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101142080.png b/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101142080.png deleted file mode 100644 index 5752bbd..0000000 --- a/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101142080.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:086d2b6fb4c74ab1986cd23ca4e2bd8d4b2922530a19415b463c80a6b5c5b5fe -size 74524 diff --git a/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101142080.webp b/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101142080.webp new file mode 100644 index 0000000..a71188a --- /dev/null +++ b/YaeBlog/source/posts/whats-new-of-dotnet-8/image-20231122101142080.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72e444663b11307e6611b134f6133e5c3a918b390703542b509cf518ce4352e5 +size 13014 diff --git a/YaeBlog/source/posts/wsl-setup-csapp.md b/YaeBlog/source/posts/wsl-setup-csapp.md index 9b2c144..5641470 100644 --- a/YaeBlog/source/posts/wsl-setup-csapp.md +++ b/YaeBlog/source/posts/wsl-setup-csapp.md @@ -1,12 +1,12 @@ --- title: 利用WSL设置CSAPP实验环境 +date: 2022-09-03T19:02:58.0000000 tags: - - 技术笔记 - - Linux -typora-root-url: wsl-setup-csapp -date: 2022-09-03 19:02:58 +- 技术笔记 +- Linux --- + `CSAPP`这本书为自学的学生们提供了不少的`LAB`供大家在联系中提高,但是这些`LAB`的编写普遍需要一个`Linux`的实验环境,但是目前大多数人手中的环境都是`Windows`平台,没有办法原生的运行这些`LAB`。在以前的实践中,这个问题往往是通过安装虚拟机来解决的,但是现在我们有了更好的解决方案——`Windows Subsystem for Linux`,简称`WSL`。 @@ -29,7 +29,7 @@ wsl --install 回车,等待所有的进度条都走完,重新启动系统。 -![设置的画面](1.png) +![设置的画面](1.webp) 在重启完成之后,Ubuntu系统会自动启动并完成一系列的配置,在其中需要你为这个系统设置一个用户。输入这个用户的名称和密码即可。 @@ -59,7 +59,7 @@ sudo apt update 测试换源是否成功。 -![证书错误](2.png) +![证书错误](2.webp) 如果在换源的过程中报错`Certificate verification failed`,可以将配置文件中的所有`https`更改为`http`来临时解决。 @@ -160,7 +160,7 @@ gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1) 如果遇到报错,部分头文件未找到: -![Error](3.png) +![Error](3.webp) 不要慌张,这是正常的。 diff --git a/YaeBlog/source/posts/wsl-setup-csapp/1.png b/YaeBlog/source/posts/wsl-setup-csapp/1.png deleted file mode 100644 index 0b59632..0000000 --- a/YaeBlog/source/posts/wsl-setup-csapp/1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a392a744186b24dcb0b2fb78ad6d7ca2575df3be59fd229076159353394421c7 -size 377521 diff --git a/YaeBlog/source/posts/wsl-setup-csapp/1.webp b/YaeBlog/source/posts/wsl-setup-csapp/1.webp new file mode 100644 index 0000000..63f928a --- /dev/null +++ b/YaeBlog/source/posts/wsl-setup-csapp/1.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa53cc3c0ea32b6eb58619436b78a3fc786498b8723fc27d350dd1b3f986a5f4 +size 27920 diff --git a/YaeBlog/source/posts/wsl-setup-csapp/2.png b/YaeBlog/source/posts/wsl-setup-csapp/2.png deleted file mode 100644 index 796829e..0000000 --- a/YaeBlog/source/posts/wsl-setup-csapp/2.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0637be96b0f33b85cb415a185f6c46d07d46e9938a1b73deb50494b0b8ecdf11 -size 179838 diff --git a/YaeBlog/source/posts/wsl-setup-csapp/2.webp b/YaeBlog/source/posts/wsl-setup-csapp/2.webp new file mode 100644 index 0000000..caf9c78 --- /dev/null +++ b/YaeBlog/source/posts/wsl-setup-csapp/2.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c860be85e00aa27a9b776f9ea8041b64b514113adbad53057a22e4a2f81f8af5 +size 182264 diff --git a/YaeBlog/source/posts/wsl-setup-csapp/3.png b/YaeBlog/source/posts/wsl-setup-csapp/3.png deleted file mode 100644 index 0d835e1..0000000 --- a/YaeBlog/source/posts/wsl-setup-csapp/3.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17b13932a250644377d0a4692ec15b68b0d469da2f5743fbb861e4e98f0e389f -size 106136 diff --git a/YaeBlog/source/posts/wsl-setup-csapp/3.webp b/YaeBlog/source/posts/wsl-setup-csapp/3.webp new file mode 100644 index 0000000..56967d9 --- /dev/null +++ b/YaeBlog/source/posts/wsl-setup-csapp/3.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53d6b2e66cec1cc60f0c194e09d05ec987eea42527c554e52496366deccfb618 +size 18364 From d0a4f4b76b5fa2e4dd113b05d2860a5905e2734e Mon Sep 17 00:00:00 2001 From: jackfiled Date: Tue, 25 Mar 2025 15:17:34 +0800 Subject: [PATCH 16/41] blog: update msbuild-generate-files --- .../source/posts/msbuild-generate-files.md | 30 +++++++++++++++++++ .../image-20250325150841442.webp | 3 ++ 2 files changed, 33 insertions(+) create mode 100644 YaeBlog/source/posts/msbuild-generate-files/image-20250325150841442.webp diff --git a/YaeBlog/source/posts/msbuild-generate-files.md b/YaeBlog/source/posts/msbuild-generate-files.md index a823395..8fcd709 100644 --- a/YaeBlog/source/posts/msbuild-generate-files.md +++ b/YaeBlog/source/posts/msbuild-generate-files.md @@ -6,6 +6,7 @@ tags: - dotnet --- + 如何使用`MSBuild`将构建过程中生成文件复制到生成目录中? @@ -115,3 +116,32 @@ tags: 经过测试,这套生成逻辑在`blazor`类库环境下也可以正常运行,类库的文件会被正确地生成到`wwwroot/_content//`文件夹下面。 +### 新的问题 + +在上述代码合并之后,我在后续开发过程中却遇到的了新的问题:在开发环境下项目运行的目录是源代码目录,而此时的`wwwroot`目录下面没有`tailwind.g.css`文件,此时网站再次丢失了样式,而如果使用`pnpm tailwindcss -i wwroot/tailwind.css -o wwwroot/tailwind.g.css`生成文件的话,却会遇到构建错误: + +![image-20250325150841442](./msbuild-generate-files/image-20250325150841442.webp) + +这是因为`.NET SDK`也会尝试将已经存在的`wwwroot/tailwind.g.css`复制到输出文件中,这就会造成冲突。 + +因此为了让开发环境和测试环境可以共存,我让`TailwindGenerate`目标只在`dotnet publish`运行,而在开发环境中使用`pnpm tailwindcss`手动生成`CSS`文件。 + +```xml + + + + + + + + + + + + + + + + +``` + diff --git a/YaeBlog/source/posts/msbuild-generate-files/image-20250325150841442.webp b/YaeBlog/source/posts/msbuild-generate-files/image-20250325150841442.webp new file mode 100644 index 0000000..d76a83c --- /dev/null +++ b/YaeBlog/source/posts/msbuild-generate-files/image-20250325150841442.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5ad19fed6a5070df683cd33dff436198ca19e7a0db23eb1348ddee1df7c6d7d +size 21830 From 22d28e763dde4a042ab5f6ef0029b3ec2c7ec0a6 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 28 Mar 2025 01:07:08 +0800 Subject: [PATCH 17/41] feat: nuget package depends on operating system --- YaeBlog/YaeBlog.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/YaeBlog/YaeBlog.csproj b/YaeBlog/YaeBlog.csproj index 133503a..29ba300 100644 --- a/YaeBlog/YaeBlog.csproj +++ b/YaeBlog/YaeBlog.csproj @@ -1,7 +1,8 @@ - + + From a254d0123d8e4ec251d241ba00c54135c4327a8a Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 28 Mar 2025 01:07:35 +0800 Subject: [PATCH 18/41] blog: hpc-2025-parallel-computing --- .../posts/hpc-2025-parallel-computing.md | 366 ++++++++++++++++++ .../image-20250313193604905.webp | 3 + .../image-20250320200524155.webp | 3 + .../image-20250327194045418.webp | 3 + .../image-20250327194312962.webp | 3 + 5 files changed, 378 insertions(+) create mode 100644 YaeBlog/source/posts/hpc-2025-parallel-computing.md create mode 100644 YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250313193604905.webp create mode 100644 YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250320200524155.webp create mode 100644 YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250327194045418.webp create mode 100644 YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250327194312962.webp diff --git a/YaeBlog/source/posts/hpc-2025-parallel-computing.md b/YaeBlog/source/posts/hpc-2025-parallel-computing.md new file mode 100644 index 0000000..d24b419 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-parallel-computing.md @@ -0,0 +1,366 @@ +--- +title: High Performance Computing 25 SP Dichotomy of Parallel Computing Platforms +date: 2025-03-28T01:03:32.2187720+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + + +Designing algorithms is always the hardest. + + + +Flynn's classical taxonomy: + +- SISD +- SIMD +- MISD +- MIMD + +Multiple instruction and multiple data is currently the most common type of parallel computer. + +> A variant: single program multiple data(SPMD). + +## Dichotomy of Parallel Computing Platforms + +Based on the logical and physical organization of parallel platforms. + +Logical organization (from a programmer's perspective): + +- Control structure: ways of expressing parallel tasks. +- Communication model: interactions between tasks. + +Hardware organization: + +- Architecture +- Interconnection networks. + +Control Structure of Parallel Platform: parallel tasks can be specified at various levels of granularity. + +Communication Model: **Shared address space platforms**. Support a common data space that is accessible to all processors. Two types of architectures: + +- Uniform memory access (UMA) +- Non-uniform memory access(NUMA) + +> NUMA and UMA are defined in term of memory access times not the cache access times. + +![image-20250313193604905](./hpc-2025-parallel-computing/image-20250313193604905.webp) + +NUMA and UMA: + +- The distinction between NUMA and UMA platforms is important from the point of view of algorithm design. + +- Programming these platforms is easier since reading and writing are implicitly visible to other processors. + +- Caches is such machines requires coordinated access to multiple copies. + + > Leads to cache coherence problem. + +- A weaker model of these machines provides an address map but not coordinated access. + +**Global Memory Space**: + +- Easy to program. + +- Read-only interactions: + + Invisible to programmers. + + Same as in serial programs. + +- Read/write interactions: + + Mutual exclusion for concurrent access such as lock and related mechanisms. + +- Programming paradigms: Threads/Directives. + +Caches in shared-address-space: + +- Address translation mechanism to locate a memory word in the system. +- Well-defined semantics over multiple copies(**cache coherence**). + +> Shared-address-space vs shared memory machine: +> +> Shared address space is a programming abstraction. +> +> Shared memory machine is a physical machine attribute. + +Distributed Shared Memory(DSM) or Shared Virtual Memory(SVM): + +- Page-based access control: leverage the virtual memory support and manage main memory as a fully associative cache on the virtual address space by embedding a coherence protocol in the page fault handler. +- Object based access control: flexible but no false sharing. + +## Parallel Algorithm Design + +Steps in parallel algorithm design: + +- Identifying portions of the work that can be performed concurrently. +- Mapping the concurrent pieces of work onto multiple processors running in parallel. +- Distributing the input, output and intermediate data associated with the program. +- Managing accesses to data shared by multiple processors. +- Synchronizing the processors at various stages of the parallel program execution. + +### Decomposition + +Dividing a computation into smaller parts some or all of which may be executed in parallel. + +Tasks: programmer-defined units with arbitrary size and is indivisible. + +Aim: **reducing execution time** + +Ideal decomposition: + +- All tasks have similar size. +- Tasks are **not** waiting for each other **not** sharing resources. + +Dependency graphs: + +Task dependency graph: an abstraction to express dependencies among tasks and their relative order of execution. + +- Directed acyclic graphs. +- Nodes are tasks. +- Directed edges: dependencies amongst tasks. + +> The fewer directed edges, the better as parallelism. + +Granularity: + +The granularity of the decomposition: the number and size of tasks into which a problem is decomposed. + +- Fine-grained: a large number of small tasks. +- Coarse-grained: a small number of large tasks. + +Concurrency: + +**maximum degree of concurrency** + +**Average degree of concurrency** + +The critical path determines the average degree of concurrency. + +Critical path is the longest directed path between any pair of start and finish nodes. So a shorter critical path favors a higher degree of concurrency. + +**Limited Granularity**: + +It may appear that increasing the granularity of decomposition will utilize the resulting concurrency. + +But there is a inherent bound on how fine-grained a decomposition a problem permits. + +Speedup: + +The ratio of serial to parallel execution time. Restrictions on obtaining unbounded speedup from: + +- Limited granularity. +- Degree of concurrency. +- Interaction among tasks running on different physical processors. + +Processor: + +Computing agent that performs tasks, an abstract entity that uses the code and data of a tasks to produce the output of the task within a finite amount of time. + +Mapping: the mechanism by which tasks are assigned to processor for execution. The task dependency and task interaction graphs play an important role. + +Decomposition techniques: + +Fundamental steps: split the computations to be performed into a set of tasks for concurrent execution. + +1. Recursive decomposition. + + A method for inducing concurrency in problems that can be solved using the **divide-and-conquer** strategy. + +2. Data decomposition. + + A method for deriving concurrency in algorithms that operate on large data structures. + + The operations performed by these tasks on different data partitions. + + Can be partitioning output data and partitioning input data or even partitioning intermediate data. + +3. Exploratory decomposition. + + Decompose problems whose underlying computations correspond to a search of a space for solutions. + + Exploratory decomposition appears similar to data decomposition. + +4. Speculative decomposition. + + Used when a program may take one of many possible computationally significant branches depending on the output of preceding computation. + + Similar to evaluating branches in a *switch* statement in `C` as evaluate multiple branches in parallel and correct branch will be used and other branches will be discarded. + + The parallel run time is smaller than the serial run time by the amount of time to evaluate the condition. + +### Characteristics of Tasks + +**Task generation**: + +- Static: all the tasks are known before the algorithm starts executing. +- Dynamic: the actual tasks and the task dependency graph are not explicitly available at priori. +- Either static or dynamic. + +**Task Sizes**: + +The relative amount of time required t complete the task. + +- Uniform +- Non-uniform + +The knowledge of task sizes will influence the choice of mapping scheme. + +**Inter-Task Interactions**: + +- Static versus dynamic. +- Regular versus irregular. +- Read-only versus read-write +- One-way versus two-way. + +### Mapping Techniques + +Mapping techniques is for loading balancing. + +Good mappings: + +- Reduce the interaction time. +- Reduce the idle time. + +![image-20250320200524155](./hpc-2025-parallel-computing/image-20250320200524155.webp) + +There are two mapping methods: + +- **Static Mapping**: determined by programming paradigm and the characteristics of tasks and interactions. + + Static mapping is often used in conjunction with *data partitioning* and *task partitioning*. + +- **Dynamic Mapping**: distribute the work among processors during the execution. Also referred as dynamic load-balancing. + + The **centralized scheme** as all the executable tasks are maintained in a common central data structure and distributed by a special process or a subset of processes as **master** process. + + Centralized scheme always means easy to implement but with limited scalability. + + The **distributed scheme** as the set of executable tasks are distributed among processes which exchange tasks at run time to balance work. + +**Minimize frequency of interactions**: + +There is a relatively high startup cost associated with each interaction on many architectures. + +So restructure the algorithm such that shared data are accessed and used in large pieces. + +**Minimize contention and hot spots**: + +Contention occurs when multiple tasks try to access the same resources concurrently. + +And centralized scheme for dynamic mapping are a frequent source of contention so use the distributed mapping schemes. + +**Overlapping computations with interactions**: + +When waiting for shared data, do some useful computations. + +- Initiate an interaction early enough to complete before it needed. +- In dynamic mapping schemes, the process can anticipate that it is going to run out of work and initiate a work which transfers interaction in advance. + +Overlapping computations with interaction requires support from the programming paradigm, the operating system and the hardware. + +- Disjoint address-space paradigm: non-blocking message passing primitives. +- Share address-space paradigm: prefetching hardware which can anticipate the memory addresses and initiate access in advance of when they are needed. + +**Replicating data or computations**: + +Multiple processors may require frequent read-only access to shared data structure such as a hash-table. + +For different paradigm: + +- Share address space use cache. +- Message passing: remote data accesses are more expensive and harder than local accesses. + +Data replication increases the memory requirements. In some situation, it may be more cost-effective to compute these intermediate results than to get then from another place. + +**Using optimized collective interaction operations**: + +Collective operations are like: + +- Broadcasting some data to all processes. +- Adding up numbers each belonging to a different process. + +### Parallel Algorithm Model + +The way of structuring parallel algorithm by + +- Selecting a decomposition +- Selecting a mapping technique. +- Applying the appropriate strategy to minimize interactions. + +**Data parallel model**: + +The tasks are statically or semi-statically mapped onto processes and each task performs similar operations on different data. + +Example: matrix multiplication. + +**Task graph model**: + +The interrelations among the tasks are utilized to promote locality or to reduce interaction costs. + +Example: quick sort, sparse matrix factorization and many other algorithms using divide-and-conquer decomposition. + +**Work pool model**: + +Characterized by a dynamic mapping of task onto processes for load balancing. + +Example: parallelization of loops by chunk scheduling. + +**Master-slave model** : + +One or more master processes generate work and allocate it to worker processes. + +**Pipeline or producer-consumer model**: + +A stream of data is passed on through a succession of processes, each of which performs some tasks. + +### Analytical Modeling of Parallel Programs + +**Performance evaluation**: + +Evaluation in terms of execution time. + +A parallel system is the combination of an algorithm and the parallel architecture on which it is implemented. + +**Sources of overhead in parallel program**: + +A typical execution includes: + +- Essential computation + + Computation that world be performed by the serial program for solving the same problem instance. + +- Interprocess communication + +- Idling + +- Excess computation + + Computation which not performed by the serial program. + +**Performance metrics for parallel system**: + +- Execution time +- Overhead function +- Total overhead +- Speedup + +> For a given problem, more than one sequential algorithm may be available. + +Theoretically speaking, speed up can never exceed the number of PE. + +If super linear speedup: the work performed by a serial program is greater than its parallel formulation, maybe hardware features that put the serial implementation at a disadvantage. + +**Amdahl's Law**: + +![image-20250327194045418](./hpc-2025-parallel-computing/image-20250327194045418.webp) + +The overall performance improvement gained by optimizing a single part of a system is limited by the fraction of time that the improved part is actually used. + +Efficiency: a measure of the fraction of time for which a PE is usefully employed. + +Cost: the product of parallel run time and the number of processing elements used. + +![image-20250327194312962](./hpc-2025-parallel-computing/image-20250327194312962.webp) diff --git a/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250313193604905.webp b/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250313193604905.webp new file mode 100644 index 0000000..7583add --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250313193604905.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4cee39cf06e317dc23379d237149a3022645d48074421e011ee0962867733fc7 +size 21450 diff --git a/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250320200524155.webp b/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250320200524155.webp new file mode 100644 index 0000000..6461154 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250320200524155.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcb8c580e257a5082ff13ba56a5ebb566e3d3f8d2d5b6e5cc30a303fe638c052 +size 18076 diff --git a/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250327194045418.webp b/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250327194045418.webp new file mode 100644 index 0000000..c373f23 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250327194045418.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d29035641271b3afdb031440da7a492678dade7c013326cdec042dab1d80d5e7 +size 3788 diff --git a/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250327194312962.webp b/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250327194312962.webp new file mode 100644 index 0000000..bee965a --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-parallel-computing/image-20250327194312962.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d880fa74f67c03334304803d036547b881f0f301a4495f96e2f783671b87b8e9 +size 24820 From a662ecc14be20d8f5c89efb9282d8979bc39c3f0 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Fri, 28 Mar 2025 13:44:00 +0800 Subject: [PATCH 19/41] feat: add build commit id in footer. --- .gitea/workflows/build.yaml | 2 +- YaeBlog/Components/Foonter.razor | 10 +++++++++- YaeBlog/Dockerfile | 3 +++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 15958d6..aa2bfde 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -18,7 +18,7 @@ jobs: - name: Build docker image run: | cd YaeBlog - podman build . -t registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest + podman build . -t registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest --build-arg COMMIT_ID=$(git rev-parse --short=10 HEAD) - name: Workaround to make sure podman login succeed run: | mkdir /root/.docker diff --git a/YaeBlog/Components/Foonter.razor b/YaeBlog/Components/Foonter.razor index 8a5935b..6318f05 100644 --- a/YaeBlog/Components/Foonter.razor +++ b/YaeBlog/Components/Foonter.razor @@ -7,11 +7,15 @@ 驱动。

    +

    + Build Commit # + +

    @@ -19,4 +23,8 @@ @code { private string DotnetVersion => $".NET {Environment.Version}"; + + private string BuildCommitId => Environment.GetEnvironmentVariable("COMMIT_ID") ?? "local_build"; + + private string BuildCommitUrl => $"https://git.rrricardo.top/jackfiled/YaeBlog/commit/{BuildCommitId}"; } diff --git a/YaeBlog/Dockerfile b/YaeBlog/Dockerfile index 8883495..fc49c56 100644 --- a/YaeBlog/Dockerfile +++ b/YaeBlog/Dockerfile @@ -1,5 +1,8 @@ FROM mcr.microsoft.com/dotnet/aspnet:9.0 +ARG COMMIT_ID +ENV COMMIT_ID=${COMMIT_ID} + WORKDIR /app COPY bin/Release/net9.0/publish/ ./ COPY source/ ./source/ From 0f346d9ded8c84a026697a148157320dd5f6444b Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 10 May 2025 01:15:02 +0800 Subject: [PATCH 20/41] blog: three blogs: blog: hpc-2025-distributed-system blog: hpc-2025-heterogeneous-system blog: hpc-2025-program-smp-platform --- .../posts/hpc-2025-distributed-system.md | 224 ++++++++++++++++++ .../image-20250410193527994.webp | 3 + .../image-20250417184421464.webp | 3 + .../image-20250417185200176.webp | 3 + .../image-20250417190247790.webp | 3 + .../image-20250417191509682.webp | 3 + .../image-20250417191526416.webp | 3 + .../image-20250417192453944.webp | 3 + .../image-20250424183610157.webp | 3 + .../image-20250424183629681.webp | 3 + .../image-20250424183645210.webp | 3 + .../posts/hpc-2025-heterogeneous-system.md | 80 +++++++ .../image-20250417195644624.webp | 3 + .../image-20250417200241703.webp | 3 + .../image-20250424184701573.webp | 3 + .../image-20250424185022360.webp | 3 + .../image-20250424185048036.webp | 3 + .../image-20250424185152081.webp | 3 + .../image-20250424185219673.webp | 3 + .../image-20250424185322963.webp | 3 + .../image-20250424185354247.webp | 3 + .../image-20250424185449577.webp | 3 + .../image-20250424185541483.webp | 3 + .../image-20250424190159059.webp | 3 + .../posts/hpc-2025-program-smp-platform.md | 106 +++++++++ .../image-20250327200344104.webp | 3 + .../image-20250403183104279.webp | 3 + .../image-20250403191254323.webp | 3 + .../image-20250403195750934.webp | 3 + 29 files changed, 488 insertions(+) create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system.md create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250410193527994.webp create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417184421464.webp create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417185200176.webp create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417190247790.webp create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191509682.webp create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191526416.webp create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417192453944.webp create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183610157.webp create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183629681.webp create mode 100644 YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183645210.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system.md create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417195644624.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417200241703.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424184701573.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185022360.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185048036.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185152081.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185219673.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185322963.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185354247.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185449577.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185541483.webp create mode 100644 YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424190159059.webp create mode 100644 YaeBlog/source/posts/hpc-2025-program-smp-platform.md create mode 100644 YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250327200344104.webp create mode 100644 YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403183104279.webp create mode 100644 YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403191254323.webp create mode 100644 YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403195750934.webp diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system.md b/YaeBlog/source/posts/hpc-2025-distributed-system.md new file mode 100644 index 0000000..c3df497 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system.md @@ -0,0 +1,224 @@ +--- +title: High Performance Computing 25 SP Distributed System +date: 2025-05-10T00:31:39.3109950+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + + +The motivation of distributed system is resource sharing. + + + +### Definition of a Distributed System + +- A collection of independent computers that appears to its users as a single coherent system. +- A system in which hardware and software components located at networked computers communicated and coordinate their actions on by message passing. + +Important aspects: + +- Components are autonomous. +- Virtually single systems as **transparency**. + +### Kinds of Systems + +**Clustering**: + +A cluster is a group of independent resources that are interconnected and work as a single system. + +A general prerequisite of hardware clustering is that its component systems have reasonably identical hardware and operating system to provide similar performance levels when one failed component is to be replaced by another. + +**Peer-to-Peer Network**: + +P2P system are quite popular for file sharing, content distribution and Internet telephony. + +**Grid Computing**: + +A computing grid (or *computational grid* ) provides a platform in which computing resources are organized into one or more logical pools. + +**Cloud Computing**: + +Enables clients to outsource their software usage, data storage and even the computing infrastructure to remote data centers. + +![image-20250410193527994](./hpc-2025-distributed-system/image-20250410193527994.webp) + +**Fog Computing**: + +Fog computing focuses processing efforts at the local area network end of the chain. + +**Edge Computing**: + +Edge computing takes localized processing a bit farther, push these efforts closer to the data sources. + +**Near Resources Computing**: + +While CPU becomes powerful, I/O devices too. So offload CPU for domain-specific computing. + +### Features of Distributed System + +**Transparency**: + +- Access transparency. +- Location transparency. +- Migration transparency. +- Relocation transparency. +- Replication transparency. +- Concurrency transparency. +- Failure transparency. + +**Openness**: + +- Open distributed systems: offer services according to standard rules that describe the syntax and semantics of those services. +- Services are specified through *interfaces*. + +**Scalability**: + +Size scalability: more users and more resources. + +- Centralized services: a single server for all users. +- Centralized data: a single on-line telephone book. +- Centralized algorithms: doing routing based on completed information. + +### Common Problems is Distributed Systems + +1. Leader Election +2. Mutual Exclusion +3. Time Synchronization +4. Global State +5. Multicasting +6. Replica Management + +### Time in Distributed Systems + +Atomic clocks: modern timekeepers use atomic clocks as a de facto primary standard of time. + +**Happened Before Relationship**: + +Three basic rules about the causal ordering of events, and they collectively define the *happened before* a.k.a the *causally ordered before* relationship. + +- Rule 1: Let each process have a physical clock whose value is monotonically increasing. +- Rule 2: If *a* is the event of sending a message by process *P*, and *b* is the event of receiving the same message by another process *Q*, so the a < b. +- Rule 3: a < b and b < c can lead to a < c. + +The space time diagrams show such relationship: + +![image-20250417184421464](./hpc-2025-distributed-system/image-20250417184421464.webp) + +**Logical Clocks**: + +A logical clock is an event counter that respects causal ordering. + +**Vector Clocks**: + +The primary goal of vector clocks is to detect causality, which is the major weakness of logical clocks. + +![image-20250424183610157](./hpc-2025-distributed-system/image-20250424183610157.webp) + +![image-20250424183629681](./hpc-2025-distributed-system/image-20250424183629681.webp) + +![image-20250424183645210](./hpc-2025-distributed-system/image-20250424183645210.webp) + +**Synchronization Classification**: + +Types of synchronization: + +- External synchronization +- Internal synchronization +- Phase synchronization + +> Types of clocks: +> +> - Unbounded +> - Bounded +> +> Unbounded clocks are not realistic but are easier to deal with in the design of algorithms. Real clocks are always bounded. + +**External Synchronization**: + +To maintain the reading of each clock as close to the UTC as possible. + +The NTP is an external synchronization protocol. + +**Internal Synchronization**: + +To keep the readings of a system of autonomous clocks closely synchronized with one another, despite the failure or malfunction of one or more clocks. + +Of course external synchronization implies internal synchronization. + +**Phase Synchronization**: + +Many distributed computations run in phases: in a given phase all processes execute some actions which are followed by the next phase. + +## Data Center Organization + +A data center is a facility used to house computer systems and associated components. + +![image-20250417185200176](./hpc-2025-distributed-system/image-20250417185200176.webp) + +## Cloud Computing + +Cloud computing is a specialized form of distributed computing that introduces utilization models for remotely provisioning scalable and measured resources. + +>**NIST definition**: +> +>Cloud computing is a model for enabling ubiquitous, convenient, on-demand network access to a shared pool of configurable computing resources (e.g., networks, servers, storage, applications, and services) that can be rapidly provisioned and released with minimal management effort or service provider interaction. This cloud model is composed of five essential characteristics, three service models, and four deployment models. + +![image-20250417190247790](./hpc-2025-distributed-system/image-20250417190247790.webp) + +**Cloud Characteristics**: + +- On-demand Usage +- Ubiquitous Access +- Multitenancy +- Elasticity +- Measure Usage +- Resiliency + +**Cloud Delivery Models**: + +A cloud service delivery model represents a specific pre-packaged combination of IT resources offered by a cloud provider. + +- Infrastructure as a Service `IaaS` +- Platform as a a Service `PaaS` +- Software as a Service `SaaS` + +**Hypervisor**: + +Type 1 hypervisor: + +![image-20250417191509682](./hpc-2025-distributed-system/image-20250417191509682.webp) + +Type 2 hypervisor: + +![image-20250417191526416](./hpc-2025-distributed-system/image-20250417191526416.webp) + +**CPU Virtualization**: + +Inter VT-X and AMD SVM: + +- Introduce virtualization technology processors with an extra instruction set called Virtual Machine Extensions or VMX. +- Add additional operating model for host and guest. +- Support for swapping state between guest and host. +- Support for hiding privileged state. + +![image-20250417192453944](./hpc-2025-distributed-system/image-20250417192453944.webp) + +## Big Data Processing + +**MapReduce Programming Model** + +MapReduce is based on a very simple idea for parallel processing of data-intensive applications supporting arbitrarily divisible load sharing. + +> The so-called same process multiple data (SPMD) paradigm. + +**MapReduce Logical Data Flow**: + +The input data and output data of both the Map and reduce functions has a particular structure. + +Sending computation toward data rather than sending data toward computation. + +**Resilient Distributed Dataset** + +An RDD is a read-only partitioned collection of records. + diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250410193527994.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250410193527994.webp new file mode 100644 index 0000000..aab3726 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250410193527994.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87b35cff45f7a9236e4f7ba5420bb55e10089a94c902d429e3d49028acd992ff +size 40522 diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417184421464.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417184421464.webp new file mode 100644 index 0000000..bb2b191 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417184421464.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:deee98ae1d92208ce8eb558b683b874d2591ae7e638a4aa7d8f3f39364cb069e +size 25654 diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417185200176.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417185200176.webp new file mode 100644 index 0000000..210a797 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417185200176.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32fef176f981353f1e6e1a55c3775343c1d22ae1b268a89c2788e992f90f8298 +size 28724 diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417190247790.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417190247790.webp new file mode 100644 index 0000000..e6cc808 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417190247790.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a853bc4d218f2b55f0a46200c0e1489f8b37f55ff6927cd012a90b58b7c7341 +size 22476 diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191509682.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191509682.webp new file mode 100644 index 0000000..dea8277 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191509682.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:005a0b076f873a12008ff8e629a17fc0a50c27e709f0f49e385b36c0f9de0e7a +size 18788 diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191526416.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191526416.webp new file mode 100644 index 0000000..f71cb05 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191526416.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9982b1fdb5e51d856bb16201811ae39218885363aa80e9f1b655b22c5c49fc4 +size 47384 diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417192453944.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417192453944.webp new file mode 100644 index 0000000..f0ab3aa --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417192453944.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a43f217d5bc520e84d9e3696734b2a3b8a356b0d6270fc33e772e0a051bab0f9 +size 69182 diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183610157.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183610157.webp new file mode 100644 index 0000000..370c235 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183610157.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32c0a5f07df25bb3f3365a6eda2e1a4d9a0b68dbc10526741d1e80c5e9f8d56b +size 36830 diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183629681.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183629681.webp new file mode 100644 index 0000000..a98926b --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183629681.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61b577a989170b2a17598c37df0f8675bb4688c9ea3643de360d88366abf4266 +size 65378 diff --git a/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183645210.webp b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183645210.webp new file mode 100644 index 0000000..bcbdfe6 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183645210.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee1f195051b08d90e9155ea6a1ea9d4ed2cb35f2284e7522da481400f667a4e3 +size 29944 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system.md b/YaeBlog/source/posts/hpc-2025-heterogeneous-system.md new file mode 100644 index 0000000..54ded24 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system.md @@ -0,0 +1,80 @@ +--- +title: High Performance Computing 25 SP Heterogeneous Computing +date: 2025-05-10T00:36:20.5391570+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + + +Heterogeneous Computing is on the way! + + + +## GPU Computing Ecosystem + +CUDA: NVIDIA's Architecture for GPU computing. + +![image-20250417195644624](./hpc-2025-heterogeneous-system/image-20250417195644624.webp) + +## Internal Buses + +**HyperTransport**: + +Primarily a low latency direct chip to chip interconnect, supports mapping to board to board interconnect such as PCIe. + +**PCI Expression** + +Switched and point-to-point connection. + +**NVLink** + +![image-20250417200241703](./hpc-2025-heterogeneous-system/image-20250417200241703.webp) + +**OpenCAPI** + +Heterogeneous computing was in the professional world mostly limited to HPC, in the consumer world is a "nice to have". + +But OpenCAPI is absorbed by CXL. + +## CPU-GPU Arrangement + +![image-20250424184701573](./hpc-2025-heterogeneous-system/image-20250424184701573.webp) + +#### First Stage: Intel Northbrige + +![image-20250424185022360](./hpc-2025-heterogeneous-system/image-20250424185022360.webp) + +### Second Stage: Symmetric Multiprocessors: + +![image-20250424185048036](./hpc-2025-heterogeneous-system/image-20250424185048036.webp) + +### Third Stage: Nonuniform Memory Access + +And the memory controller is integrated directly in the CPU. + +![image-20250424185152081](./hpc-2025-heterogeneous-system/image-20250424185152081.webp) + +So in such context, the multiple CPUs is called NUMA: + +![image-20250424185219673](./hpc-2025-heterogeneous-system/image-20250424185219673.webp) + +And so there can be multi GPUs: + +![image-20250424185322963](./hpc-2025-heterogeneous-system/image-20250424185322963.webp) + +### Fourth Stage: Integrated PCIe in CPU + +![image-20250424185354247](./hpc-2025-heterogeneous-system/image-20250424185354247.webp) + +And there is such team *integrated CPU*, which integrated a GPU into the CPU chipset. + +![image-20250424185449577](./hpc-2025-heterogeneous-system/image-20250424185449577.webp) + +And the integrated GPU can work with discrete GPUs: + +![image-20250424185541483](./hpc-2025-heterogeneous-system/image-20250424185541483.webp) + +### Final Stage: Multi GPU Board + +![image-20250424190159059](./hpc-2025-heterogeneous-system/image-20250424190159059.webp) diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417195644624.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417195644624.webp new file mode 100644 index 0000000..88ea13c --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417195644624.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c900e50ff87390ebda85f9d67e3a38ca4cf44af8e05738bdab048a01eb9922a +size 20734 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417200241703.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417200241703.webp new file mode 100644 index 0000000..0caecc5 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417200241703.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0dae80ace7bc2918887f3c343ce2642c1ef98197a02d66ee679f767a769ef242 +size 49858 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424184701573.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424184701573.webp new file mode 100644 index 0000000..4ab29af --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424184701573.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c674ca7e69cbcf22fad70f3d50eb74ac00d087f3963f0efcf989d33057877670 +size 55266 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185022360.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185022360.webp new file mode 100644 index 0000000..01e51f2 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185022360.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:facf46bba579b084af9c30bbbfa67ca4861091179b4dcdac625140eba063dece +size 13764 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185048036.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185048036.webp new file mode 100644 index 0000000..0b76a77 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185048036.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:464e7414f5a4dc821561b985aab944e633dd4dab693fc6c75da245c5a969b7c6 +size 11604 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185152081.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185152081.webp new file mode 100644 index 0000000..99e5944 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185152081.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c8fb5d6381ab77461c0e8ea2fcc25887d1e80d8034687cc98b91a93bee0d03e +size 8946 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185219673.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185219673.webp new file mode 100644 index 0000000..771fa6b --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185219673.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8466f02dbf307374fd2b16d3ceef32759af45016808af2d06347be05a6aed9c2 +size 11440 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185322963.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185322963.webp new file mode 100644 index 0000000..43577d0 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185322963.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1ab29cfe58e3cb5137c5e7d7fe167f82a5a3c9331cbb29019b6614a5ae2dffc +size 14300 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185354247.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185354247.webp new file mode 100644 index 0000000..4e97e9a --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185354247.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:180ce40db7a628338e349c5443133b5448d8e90c676b0a6cdc60e7ec49ed4922 +size 11668 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185449577.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185449577.webp new file mode 100644 index 0000000..9e05280 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185449577.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8aa7937e9ecc5eefda100ab6eef58bbb02aa34e89d551d7d6113732fb93e2db3 +size 7570 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185541483.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185541483.webp new file mode 100644 index 0000000..54a0fdb --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185541483.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1203d511a25cc66168356ff8356dc0d5527651b6f38acd2124e1284408e3277 +size 10742 diff --git a/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424190159059.webp b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424190159059.webp new file mode 100644 index 0000000..2118571 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424190159059.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f847c355799225bdf76a8a6c52bcbff06d937042ea3de1a41fd31f4ad10b43dd +size 17618 diff --git a/YaeBlog/source/posts/hpc-2025-program-smp-platform.md b/YaeBlog/source/posts/hpc-2025-program-smp-platform.md new file mode 100644 index 0000000..1a4c19e --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-program-smp-platform.md @@ -0,0 +1,106 @@ +--- +title: High Performance Computing 25 SP Programming SMP Platform +date: 2025-05-10T00:17:26.5784020+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + + + +Sharing address space brings simplification. + + + +### Shared Address Space Programming Paradigm + +Vary on mechanism for data sharing, concurrency models and support for synchronization. + +- Process based model +- Lightweight processes and threads. + +### Thread + +A thread is a single stream of control in the flow of a program. + +**Logical memory model of a thread**: + +All memory is globally accessible to every thread and threads are invoked as function calls. + +![image-20250327200344104](./hpc-2025-program-smp-platform/image-20250327200344104.webp) + +Benefits of threads: + +- Take less time to create a new thread than a new process. +- Less time to terminate a thread than a process. + +The taxonomy of thread: + +- User-Level Thread(ULT): The kernel is not aware of the existence of threads. All thread management is done by the application using a thread library. Thread switching does not require kernel mode privileges and scheduling is application specific. +- Kernel-Level Thread(KLT): All thread management is done by kernel. No thread library but an API to the kernel thread facility. Switching between threads requires the kernel and schedule on a thread basis. +- Combined ULT and KLT approaches. Combines the best of both approaches. + +![image-20250403183104279](./hpc-2025-program-smp-platform/image-20250403183104279.webp) + +## PThread Programming + +Potential problems with threads: + +- Conflicting access to shared memory. +- Race condition occur. +- Starvation +- Priority inversion +- Deadlock + +### Mutual Exclusion + +Mutex locks: implementing critical sections and atomic operations. + +Two states: locked and unlocked. At any point of time, only one thread can lock a mutex lock. + +### Producer-Consumer Work Queues + +The producer creates tasks and inserts them into a work-queue. + +The consumer threads pick up tasks from the task queue and execute them. + +Locks represent serialization points since critical sections must be executed by threads one after the other. + +**Important**: Minimize the size of critical sections. + +### Condition Variables for Synchronization + +The `pthread_mutex_trylock` alleviates the idling time but introduce the overhead of polling for availability of locks. + +An interrupt driven mechanism as opposed to a polled mechanism as the availability is signaled. + +A **condition variable**: a data object used for synchronizing threads. Block itself until specified data reaches a predefined state. + +When a thread performs a condition wait, it's not runnable as not use any CPU cycles but a mutex lock consumes CPU cycles as it polls for the locks. + +**Common Errors**: One cannot assume any order of execution, must be explicitly established by mutex, condition variables and joins. + +## MPI Programming + +Low cost message passing architecture. + +![image-20250403191254323](./hpc-2025-program-smp-platform/image-20250403191254323.webp) + +Mapping of MPI Processes: + +MPI views the processes as a one-dimensional topology. But in parallel programs, processes are arranged in higher-dimensional topologies. So it is required to map each MPI process to a process in the higher dimensional topology. + +Non-blocking Send and Receive: + +`MPI_ISend` and `MPI_Irecv` functions allocate a request object and return a pointer to it. + +## OpenMP + +A standard for directive based parallel programming. + +Thread based parallelism and explicit parallelism. + +Use fork-join model: + +![image-20250403195750934](./hpc-2025-program-smp-platform/image-20250403195750934.webp) + diff --git a/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250327200344104.webp b/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250327200344104.webp new file mode 100644 index 0000000..258b9c4 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250327200344104.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81a2610afcd3913bfeb523eb861d63680fa81b7a9741c1cf10c4ecca16081558 +size 25110 diff --git a/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403183104279.webp b/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403183104279.webp new file mode 100644 index 0000000..4519ed0 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403183104279.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:305c571375a81ada7dbaf9e3c32afffecad3a9b2a60d7a148a94ecf93336555d +size 33248 diff --git a/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403191254323.webp b/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403191254323.webp new file mode 100644 index 0000000..67a253b --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403191254323.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:061682bb2fbc7c9eab831d262dc6419a1d8d58a0c40bc012be7d8a5a091c3bf2 +size 63130 diff --git a/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403195750934.webp b/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403195750934.webp new file mode 100644 index 0000000..48a67d5 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-program-smp-platform/image-20250403195750934.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:874702176e18c0e122750042b0985c52e2eccb0d3444323d3d4601318535937c +size 24408 From eedfc1ffcedd511e665a0ed246bc23777a1a2813 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Tue, 27 May 2025 14:25:27 +0800 Subject: [PATCH 21/41] blog: linux distribution from zero --- .../posts/linux-distribution-from-zero.md | 306 ++++++++++++++++++ .../image-20250325160729310.webp | 3 + .../image-20250325161310820.webp | 3 + .../image-20250527134233659.webp | 3 + .../image-20250527134421403.webp | 3 + .../image-20250527134540583.webp | 3 + .../image-20250527135748547.webp | 3 + 7 files changed, 324 insertions(+) create mode 100644 YaeBlog/source/posts/linux-distribution-from-zero.md create mode 100644 YaeBlog/source/posts/linux-distribution-from-zero/image-20250325160729310.webp create mode 100644 YaeBlog/source/posts/linux-distribution-from-zero/image-20250325161310820.webp create mode 100644 YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134233659.webp create mode 100644 YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134421403.webp create mode 100644 YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134540583.webp create mode 100644 YaeBlog/source/posts/linux-distribution-from-zero/image-20250527135748547.webp diff --git a/YaeBlog/source/posts/linux-distribution-from-zero.md b/YaeBlog/source/posts/linux-distribution-from-zero.md new file mode 100644 index 0000000..4a06941 --- /dev/null +++ b/YaeBlog/source/posts/linux-distribution-from-zero.md @@ -0,0 +1,306 @@ +--- +title: 从零开始的Linux发行版生活 +date: 2025-05-27T14:22:45.9208348+08:00 +tags: +- Linux +- 技术笔记 +--- + + + 总有些时候我们需要自己组装Linux操作系统,比如交叉编译、嵌入式开发和可信执行环境开发等等场景。本文便介绍如何使用Arch Linux作为基础在`riscv`架构上组装操作系统并使用QEMU运行。 + + + +## 初始化根文件系统 + +`rootfs`是Linux系统中除了内核之外的其他文件的总和,例如`/usr`和`/etc`中重要的系统文件均属于`rootfs`的范围。在进行Linux系统的开发时,同一架构的`rootfs`之间基本上可以互换,例如可以把Arch Linux的`rootfs`替换到`ubuntu`系统中,而内核由于硬件的敏感性,通常需要使用特定厂商提供的内核(在更改合入upstream之前)。 + +> 实际上,除了各个发行版对于内核的修改,各个发行版之间主要的不同就是rootfs的不同。 + +首先创建一个`rootfs`文件夹并修改权限为`root`。 + +```bash +mkdir rootfs +sudo chown root:root ./rootfs +``` + +然后使用`pacstrap`这个`pacman`的初始化工具在`rootfs`安装`base`软件包,最好也顺便装一个`vim`。 + +```bash +sudo pacstrap \ + -C /usr/share/devtools/pacman.conf.d/extra-riscv64.conf + -M ./rootfs \ + base vim +``` + +`extra-riscv64.conf`是在`archlinuxcn/devtools-riscv64`软件包中提供的便利工具,其中包括了`archriscv`该移植的`pacman.conf`文件,当然一般推荐修改一下该文件的镜像站点,以提高安装的速度。 + +然后清理一下`pacman`的缓存文件,缩小`rootfs`的大小,尤其是考虑到后面因为各种操作失误可能会反复解压`rootfs`文件。 + +```bash +sudo pacman \ + --sysroot ./rootfs \ + --sync --clean --clean +``` + +然后设置一下该`rootfs`的`root`账号密码: + +```bash +sudo usermod --root $(realpath ./rootfs) --password $(openssl passwd -6 "$password") root +``` + +就可以将`rootfs`打包为压缩包文件备用了。 + +```bash +sudo bsdtar --create \ + --auto-compress --options "compression-level=9"\ + --xattrs --acls\ + -f archriscv-rootfs.tar.zst -C rootfs/ . +``` + +## 初始化虚拟机镜像 + +首先,创建一个`qcow2`格式的QEMU虚拟机磁盘镜像: + +```bash +qemu-img create -f qcow2 archriscv.img 10G +``` + +其中磁盘的大小可以自行定义。 + +为了能够像正常的磁盘一样进行读写,需要将该文件映射到一个块设备,而这通过`qemu-nbd`程序实现。首先需要加载该程序需要使用的内核驱动程序: + +```bash +sudo modprobe nbd max_part=8 +``` + +命令中的`max_part`指定了最多能够挂载的块设备(文件)个数。然后将该文件虚拟化为一个块设备: + +```bash +sudo qemu-nbd -c /dev/nbd0 archriscv.img +``` + +挂载完毕之后就可以进行初始化虚拟机磁盘镜像的工作了。初始化虚拟机镜像主要涉及到如下几步: + +- 格式化磁盘 +- 安装内核 +- 设置引导程序 + +其中格式化磁盘和后续需要使用的启动引导方式有关系,当使用U-boot这一常用的嵌入式引导系统进行引导时,只需要将磁盘格式化为单个分区即可,只需要在该分区中设置`extlinux/extlinux.conf`文件,至于磁盘的分区表格式是`GPT`还是`MBR`无关紧要。而如果是使用UEFI引导,则需要使用`GPT`分区表,并创建一个ESP(EFI System Partition)分区。这里就以使用UEFI引导的格式化磁盘作为示例,硬盘分区如下表所示: + +| 分区 | 格式 | 挂载点 | 大小 | +| ----------- | ----- | ------ | ---------- | +| /dev/nbd0p1 | FAT32 | /boot | 512M | +| /dev/nbd0p2 | EXT4 | / | 余下的空间 | + +在使用`fdisk`完成磁盘的分区之后,进行格式化并挂载到当前的`mnt`目录中: + +```bash +sudo mkfs.fat -F 32 /dev/nbd0p1 +sudo mkfs.ext4 /dev/nbd0p2 +sudo mkdir mnt +sudo mount /dev/nbd0p2 mnt +sudo mkdir mnt/boot +sudo mount /dev/nbd0p1 mnt/boot +``` + +挂载完成之后解压上一步中备好的`rootfs`: + +```bash +cd mnt +sudo bsdtar -kpxf ../archriscv.tar.zst +``` + +然后使用`systemd-nspawn`工具进入`rootfs`中调用`pacman`安装内核: + +```bash +sudo systemd-nspawn -D mnt pacman \ + --nonconfirm --needed \ + -Syu linux linux-firmware +``` + +接下来分别介绍使用U-boot启动和使用UEFI启动的操作方法。 + +### 使用U-boot启动 + +为了使用U-boot启动,需要手动编译U-boot并打包到OpenSBI中作为QEMU启动的固件。 + +首先编译U-boot: + +```bash +git clone --filter=blob:none -b v2025.04 https://github.com/u-boot/u-boot.git +cd u-boot +make \ + CROSS_COMPILE=riscv64-linux-gnu- \ + qemu-riscv64_smode_defconfig +./scripts/config +make \ + CROSS_COMPILE=riscv64-linux-gnu- \ + olddefconfig +make CROSS_COMPILE=riscv64-linux-gnu- -j18 +``` + +编译好之后检查当前目录下是否存在`u-boot.bin`的固件。 + +然后去编译OpenSBI并将`u-boot.bin`打包进来: + +```bash +git clone --filter=blob:none -b v1.6 https://github.com/riscv-software-src/opensbi.git +cd opensbi +make \ + CROSS_COMPILE=riscv64-linux-gnu- \ + PLATFORM=generic \ + FW_PAYLOAD_PATH=../u-boot/u-boot.bin -j18 +``` + +编译好的三个启动固件应当在`./build/platform/generic/firmware`目录中: + +- `fw_dynamic.bin`使用启动程序设置的地址进行跳转。 +- `fw_jump.bin`跳转到一个固定的地址执行。 +- `fw_payload.bin`执行编译打包的`u-boot`文件,这也是U-boot启动所需要的。 + +编译完成之后,在`mnt`文件中创建`/boot/extlinux/extlinux.conf`文件以告知U-boot启动Linux内核的参数: + +``` +menu title Arch RISC-V Boot Menu +timeout 100 +default linux-fallback + +label linux + menu label Linux linux + kernel /vmlinuz-linux + initrd /initramfs-linux.img + append earlyprintk rw root=UUID=903944ec-a4d3-4820-ac89-c0eac37721f9 rootwait console=ttyS0,115200 + +label linux-fallback + menu label Linux linux (fallback initramfs) + kernel /vmlinuz-linux + initrd /initramfs-linux-fallback.img + append earlyprintk rw root=UUID=903944ec-a4d3-4820-ac89-c0eac37721f9 rootwait console=ttyS0,115200 +``` + +文件中的UUID可以使用如下的指令获得: + +```bash +findmnt mnt -o UUID -n +``` + +其中需要说明的是,文件中指定kernel和intird的时候使用的是`/`而不是`/boot`,这是因为虽然现在把该分区挂载到了`/boot`目录下,但是在U-boot进行启动时会将该分区挂载在`/`目录下,因此需要使用`/`。也是因为同样的原因,当只格式化为一个分区并只使用U-boot进行引导启动时,则需要将目录改为`/boot`。 + +此时即可取消挂载镜像了: + +```bash +sudo umount mnt/boot +sudo umount mnt +sudo qemu-nbd -d /dev/nbd0 +``` + +使用如下的指令即可启动虚拟机: + +```bash +#!/bin/bash + +qemu-system-riscv64 \ + -nographic \ + -machine virt \ + -smp 8 \ + -m 4G \ + -bios opensbi/build/platform/generic/firmware/fw_payload.bin \ + -device virtio-blk-device,drive=hd0 \ + -drive file=archriscv-1.img,format=qcow2,id=hd0,if=none \ + -object rng-random,filename=/dev/urandom,id=rng0 \ + -device virtio-rng-device,rng=rng0 \ + -monitor unix:/tmp/qemu-monitor,server,nowait +``` + +### 使用UEFI启动 + +使用UEFI启动,就需要编译对应的UEFI固件,即开源固件EDK2。 + +```bash +git clone -b edk2-stable202505 --recursive-submodule https://github.com/tianocore/edk2.git +export WORKSPACE=`pwd` +export GCC5_RISCV64_PREFIX=riscv64-linux-gnu- +export PACKAGES_PATH=$WORKSPACE/edk2 +export EDK_TOOLS_PATH=$WORKSPACE/edk2/BaseTools +source edk2/edksetup.sh --reconfig +make -C edk2/BaseTools -j18 +source edk2/edksetup.sh BaseTools +build -a RISCV64 --buildtarget RELEASE -p OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc -t GCC5 +``` + +编译之后得到的两份固件应该在`Build/RiscVVirtQemu/RELEASE_GCC5/FV`目录下: + +- `RISCV_VIRT_CODE.fd`固件的代码部分。 +- `RISCV_VIRT_VARS.fd`固件的数据部分,可以被UEFI工具修改。 + +在启动之前首先将这两个文件填充到32M的大小以符合QEMU对于`pflash`文件的大小要求: + +```bash +truncate -s 32M Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_CODE.fd +truncate -s 32M Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_VARS.fd +``` + +然后就可以使用如下的指令启动QEMU虚拟机了,这里复用U-boot中编译的OpenSBI固件,如果没有执行这一步可以选择删除下面指令中的`-bios`选项,使用QEMU自带的OpenSBI实现。 + +```bash +#!/bin/bash + +qemu-system-riscv64 \ + -M virt,pflash0=pflash0,pflash1=pflash1,acpi=off \ + -m 4096 -smp 8 -nographic \ + -bios opensbi/build/platform/generic/firmware/fw_dynamic.bin \ + -blockdev node-name=pflash0,driver=file,read-only=on,filename=Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_CODE.fd \ + -blockdev node-name=pflash1,driver=file,filename=Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_VARS.fd \ + -device virtio-blk-device,drive=hd0 \ + -drive file=archriscv-1.img,format=qcow2,id=hd0,if=none \ + -netdev user,id=n0 -device virtio-net,netdev=n0 \ + -monitor unix:/tmp/qemu-monitor,server,nowait +``` + +但是,这一步启动并不会进入Linux内核,这是因为还没有向UEFI注册需要启动的系统,使得UEFI可以识别到可以执行启动的磁盘。在普通的系统安装上,由于是使用安装镜像直接从UEFI启动的,在`chroot`环境中可以直接使用`grub-install`直接安装,但是在目前的`systemd-nspawn`环境中是缺少`efivarfs`等必要的文件系统的。 + +因此可以首先尝试在启动之后进入`UEFI Shell`之后,手动设置参数直接启动Linux内核。 + +![image-20250527134233659](./linux-distribution-from-zero/image-20250527134233659.webp) + +进入`UEFI Shell`之后,首先选择文件系统`FS0:`,然后使用如下的指令尝试手动启动Linux内核: + +```bash +\vmlinuz-linux initrd=\initramfs-linux.img earlyprintk rw root=UUID=903944ec-a4d3-4820-ac89-c0eac37721f9 rootwait console=ttyS0,115200 +``` + +但是可能会遇到如下的问题: + +![image-20250527134421403](./linux-distribution-from-zero/image-20250527134421403.webp) + +这里也尝试了使用`mkinitcpio`生成的Unified Kernel Image,放在`EFI/Linux`文件目录下,同样遇到了如下的问题: + +![image-20250527134540583](./linux-distribution-from-zero/image-20250527134540583.webp) + +暂时不清楚这是EDK2的问题还是这里操作的问题,至少能确定这里编译内核时是启用了`CONFIG_EFI_STUB`选项的。 + +因此这里使用`grub`方式尝试绕过这个问题,首先在`systemd-nswpan`环境中使用如下的指令安装`grub`,虽然会因为环境问题报错,但是手动查看可以发现安装脚本已经将`grubriscv64.efi`文件复制到`/boot/EFI/GRUB`目录了。 + +此时再次进入`UEFI Shell`,手动指定启动`grub`,所幸这次启动成功,此时我们再从`grub shell`中尝试启动Linux,使用的指令如下: + +```bash +linux (hd0,gpt1)/vmlinuz-linux earlyprintk rw root=UUID=903944ec-a4d3-4820-ac89-c0eac37721f9 rootwait console=ttyS0,115200 +initrd (hd0,gpt1)/initramfs-linux.img +boot +``` + +![image-20250527135748547](./linux-distribution-from-zero/image-20250527135748547.webp) + +此时就可以正常的进入完成完整的安装过程了。 + +> 首次启动的时候推荐使用`fallback initramfs`,因为在`chroot`环境中生成的驱动可能不全。如果在使用主要的`initramfs`进行启动时遇到了无法挂载真`/`目录而进入`emergency shell`,同时在该Shell中也无法发现虚拟机的磁盘,就极有可能是系统缺少对应的驱动无法挂载。 +> +> 例如在`chroot`环境中生成的`initcpio`包含如下的模块: +> +> ![image-20250325160729310](./linux-distribution-from-zero/image-20250325160729310.webp) +> +> 而在进入系统之后,重新运行`mkinitcpio`之后包含的模块如下所示: +> +> ![image-20250325161310820](./linux-distribution-from-zero/image-20250325161310820.webp) + diff --git a/YaeBlog/source/posts/linux-distribution-from-zero/image-20250325160729310.webp b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250325160729310.webp new file mode 100644 index 0000000..956531f --- /dev/null +++ b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250325160729310.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48ab3dd92f703da5ee676d072c69e393c6b7f2f169aacb52d626f1859b21d044 +size 19452 diff --git a/YaeBlog/source/posts/linux-distribution-from-zero/image-20250325161310820.webp b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250325161310820.webp new file mode 100644 index 0000000..681ccc1 --- /dev/null +++ b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250325161310820.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:903133fdccb46ef5b6c42dd30d0a0be92f0b7e93007ec3500a7327a1bda7a3de +size 16988 diff --git a/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134233659.webp b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134233659.webp new file mode 100644 index 0000000..7187f8c --- /dev/null +++ b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134233659.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbddead6d887e378caa31becd335e040b3b0bddf9ad90cb18d48e0b2fbf2e045 +size 21630 diff --git a/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134421403.webp b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134421403.webp new file mode 100644 index 0000000..f582de5 --- /dev/null +++ b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134421403.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c76d0f31d20a60aa1945fefcf7e557cb5c44c0575557916cfca586ebe34bc665 +size 19944 diff --git a/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134540583.webp b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134540583.webp new file mode 100644 index 0000000..6377a90 --- /dev/null +++ b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527134540583.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10a621726031480fca3d44e9a7c24f9e645bdb99c955c009c433b76626269104 +size 21798 diff --git a/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527135748547.webp b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527135748547.webp new file mode 100644 index 0000000..738dcfb --- /dev/null +++ b/YaeBlog/source/posts/linux-distribution-from-zero/image-20250527135748547.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:705f545ea119e2559aabab0b25061bd2cc62c92169824561df7212a36f847fc7 +size 10152 From 938fe1c71572bff9d1e02da1c02ecaff49f2f440 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 28 Jun 2025 18:24:46 +0800 Subject: [PATCH 22/41] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E8=B5=9E=E8=B5=8F=E7=A0=81=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-on: https://git.rrricardo.top/jackfiled/YaeBlog/pulls/16 --- .gitattributes | 1 + YaeBlog/Components/AppreciationCode.razor | 29 ++++++++++++++++++++++ YaeBlog/Components/LicenseDisclaimer.razor | 14 ++++++++--- YaeBlog/Models/BlogContents.cs | 7 ++---- YaeBlog/wwwroot/images/alipay-code.jpeg | 3 +++ YaeBlog/wwwroot/images/wechat-code.jpeg | 3 +++ 6 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 YaeBlog/Components/AppreciationCode.razor create mode 100644 YaeBlog/wwwroot/images/alipay-code.jpeg create mode 100644 YaeBlog/wwwroot/images/wechat-code.jpeg diff --git a/.gitattributes b/.gitattributes index f5589bc..cdc29e3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ *.png filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text *.avif filter=lfs diff=lfs merge=lfs -text *.webp filter=lfs diff=lfs merge=lfs -text diff --git a/YaeBlog/Components/AppreciationCode.razor b/YaeBlog/Components/AppreciationCode.razor new file mode 100644 index 0000000..4d3e2b0 --- /dev/null +++ b/YaeBlog/Components/AppreciationCode.razor @@ -0,0 +1,29 @@ +
    +
    + 微信赞赏码 +
    + 请我喝奶茶
    +
    +
    + +
    + 支付宝赞赏码 +
    + 请我吃晚饭
    +
    +
    + +
    diff --git a/YaeBlog/Components/LicenseDisclaimer.razor b/YaeBlog/Components/LicenseDisclaimer.razor index ab77a81..3318a39 100644 --- a/YaeBlog/Components/LicenseDisclaimer.razor +++ b/YaeBlog/Components/LicenseDisclaimer.razor @@ -1,6 +1,3 @@ -@using YaeBlog.Models -@inject BlogOptions Options -
    @@ -24,6 +21,17 @@ Ricardo's Blog ”。
    + +
    +
    +

    如果觉得不错的话,可以支持一下作者哦~

    +
    + +
    + +
    +
    +
    diff --git a/YaeBlog/Models/BlogContents.cs b/YaeBlog/Models/BlogContents.cs index 11dd937..7540078 100644 --- a/YaeBlog/Models/BlogContents.cs +++ b/YaeBlog/Models/BlogContents.cs @@ -6,10 +6,7 @@ namespace YaeBlog.Models; public record BlogContents(ConcurrentBag Drafts, ConcurrentBag Posts) : IEnumerable { - IEnumerator IEnumerable.GetEnumerator() - { - return Posts.Concat(Drafts).GetEnumerator(); - } + public IEnumerator GetEnumerator() => Posts.Concat(Drafts).GetEnumerator(); - public IEnumerator GetEnumerator() => ((IEnumerable)this).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } diff --git a/YaeBlog/wwwroot/images/alipay-code.jpeg b/YaeBlog/wwwroot/images/alipay-code.jpeg new file mode 100644 index 0000000..480598c --- /dev/null +++ b/YaeBlog/wwwroot/images/alipay-code.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9dfcb06c706d121c2ea672407843e0ad011e43531ef2ea4f2141fca2795012da +size 102287 diff --git a/YaeBlog/wwwroot/images/wechat-code.jpeg b/YaeBlog/wwwroot/images/wechat-code.jpeg new file mode 100644 index 0000000..a036e04 --- /dev/null +++ b/YaeBlog/wwwroot/images/wechat-code.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:527ba104ee72f1f7b934f7a7f8ed8cb2edf905ba39d1c0f89d986c5421871f3e +size 49545 From 94421168c617431fcbe8389a0e3bd91af8321a5c Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sun, 31 Aug 2025 13:54:08 +0800 Subject: [PATCH 23/41] blog: high-performance-computing notebook (#17) Reviewed-on: https://git.rrricardo.top/jackfiled/YaeBlog/pulls/17 --- YaeBlog/source/posts/hpc-2025-cuda.md | 370 ++++++++++++++++++ .../image-20250424192311202.webp | 3 + .../image-20250424192825010.webp | 3 + .../image-20250424193125125.webp | 3 + .../image-20250424195111341.webp | 3 + .../image-20250424195221886.webp | 3 + .../image-20250424200022880.webp | 3 + .../image-20250424200330783.webp | 3 + .../image-20250508183446257.webp | 3 + .../image-20250508183528381.webp | 3 + .../image-20250508183724162.webp | 3 + .../image-20250508183931464.webp | 3 + .../image-20250508184155087.webp | 3 + .../image-20250508184455215.webp | 3 + .../image-20250508185236920.webp | 3 + .../image-20250508185812302.webp | 3 + .../image-20250508193756274.webp | 3 + .../image-20250508194350572.webp | 3 + .../image-20250508195840957.webp | 3 + .../image-20250508195938546.webp | 3 + .../image-20250508200041910.webp | 3 + .../image-20250515183524043.webp | 3 + .../image-20250515184225475.webp | 3 + .../image-20250515184621914.webp | 3 + .../image-20250515184714663.webp | 3 + .../image-20250515185019590.webp | 3 + .../image-20250515185034758.webp | 3 + .../image-20250515185212184.webp | 3 + .../image-20250515185801775.webp | 3 + .../image-20250515190244112.webp | 3 + .../image-20250515190507199.webp | 3 + .../image-20250515190703918.webp | 3 + .../image-20250515191142384.webp | 3 + .../hpc-2025-non-stored-program-computing.md | 241 ++++++++++++ .../image-20250605185212740.webp | 3 + .../image-20250612184120333.webp | 3 + .../image-20250815093113115.webp | 3 + .../image-20250817183832472.webp | 3 + .../image-20250817184419856.webp | 3 + .../image-20250817185111521.webp | 3 + .../image-20250817185859510.webp | 3 + .../image-20250817192006784.webp | 3 + .../image-20250817194355228.webp | 3 + .../image-20250817195139631.webp | 3 + .../image-20250817195714935.webp | 3 + .../image-20250817200350750.webp | 3 + .../image-20250829210329225.webp | 3 + YaeBlog/source/posts/hpc-2025-opencl.md | 100 +++++ .../image-20250529185915068.webp | 3 + .../image-20250529191215424.webp | 3 + .../image-20250529191414250.webp | 3 + .../image-20250529191512490.webp | 3 + .../image-20250529192022613.webp | 3 + .../image-20250529192056388.webp | 3 + YaeBlog/source/posts/hpc-2025-potpourri.md | 79 ++++ .../image-20250612185019968.webp | 3 + .../image-20250612185221643.webp | 3 + .../image-20250612185743675.webp | 3 + .../image-20250612191035632.webp | 3 + .../image-20250612191118473.webp | 3 + YaeBlog/source/posts/hpc-2025-program-cuda.md | 42 ++ .../image-20250515195739382.webp | 3 + .../image-20250515195907764.webp | 3 + 63 files changed, 1006 insertions(+) create mode 100644 YaeBlog/source/posts/hpc-2025-cuda.md create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250424192311202.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250424192825010.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250424193125125.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250424195111341.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250424195221886.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250424200022880.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250424200330783.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508183446257.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508183528381.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508183724162.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508183931464.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508184155087.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508184455215.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508185236920.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508185812302.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508193756274.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508194350572.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508195840957.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508195938546.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250508200041910.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515183524043.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515184225475.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515184621914.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515184714663.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515185019590.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515185034758.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515185212184.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515185801775.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515190244112.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515190507199.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515190703918.webp create mode 100644 YaeBlog/source/posts/hpc-2025-cuda/image-20250515191142384.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing.md create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250605185212740.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250612184120333.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250815093113115.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817183832472.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817184419856.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817185111521.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817185859510.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817192006784.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817194355228.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817195139631.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817195714935.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817200350750.webp create mode 100644 YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250829210329225.webp create mode 100644 YaeBlog/source/posts/hpc-2025-opencl.md create mode 100644 YaeBlog/source/posts/hpc-2025-opencl/image-20250529185915068.webp create mode 100644 YaeBlog/source/posts/hpc-2025-opencl/image-20250529191215424.webp create mode 100644 YaeBlog/source/posts/hpc-2025-opencl/image-20250529191414250.webp create mode 100644 YaeBlog/source/posts/hpc-2025-opencl/image-20250529191512490.webp create mode 100644 YaeBlog/source/posts/hpc-2025-opencl/image-20250529192022613.webp create mode 100644 YaeBlog/source/posts/hpc-2025-opencl/image-20250529192056388.webp create mode 100644 YaeBlog/source/posts/hpc-2025-potpourri.md create mode 100644 YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185019968.webp create mode 100644 YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185221643.webp create mode 100644 YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185743675.webp create mode 100644 YaeBlog/source/posts/hpc-2025-potpourri/image-20250612191035632.webp create mode 100644 YaeBlog/source/posts/hpc-2025-potpourri/image-20250612191118473.webp create mode 100644 YaeBlog/source/posts/hpc-2025-program-cuda.md create mode 100644 YaeBlog/source/posts/hpc-2025-program-cuda/image-20250515195739382.webp create mode 100644 YaeBlog/source/posts/hpc-2025-program-cuda/image-20250515195907764.webp diff --git a/YaeBlog/source/posts/hpc-2025-cuda.md b/YaeBlog/source/posts/hpc-2025-cuda.md new file mode 100644 index 0000000..bf76da6 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda.md @@ -0,0 +1,370 @@ +--- +title: High Performance Computing 25 SP NVIDIA +date: 2025-08-31T13:50:42.8639950+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + + + +Fxxk you, NVIDIA! + + + +CPU/GPU Parallelism: + +Moore's Law gives you more and more transistors: + +- CPU strategy: make the workload (one compute thread) run as fast as possible. +- GPU strategy: make the workload (as many threads as possible) run as fast as possible. + +GPU Architecture: + +- Massively Parallel +- Power Efficient +- Memory Bandwidth +- Commercially Viable Parallelism +- Not dependent on large caches for performance + +![image-20250424192311202](./hpc-2025-cuda/image-20250424192311202.webp) + +## Nvidia GPU Generations + +- 2006: G80-based GeForce 8800 +- 2008: GT200-based GeForce GTX 280 +- 2010: Fermi +- 2012: Kepler +- 2014: Maxwell +- 2016: Pascal +- 2017: Volta +- 2021: Ampere +- 2022: Hopper +- 2024: Blackwell + +#### 2006: G80 Terminology + +SP: Streaming Processor, scalar ALU for a single CUDA thread + +SPA: Stream Processor Array + +SM: Streaming Multiprocessor, containing of 8 SP + +TPC: Texture Processor Cluster: 2 SM + TEX + +![image-20250424192825010](./hpc-2025-cuda/image-20250424192825010.webp) + +Design goal: performance per millimeter + +For GPUs, performance is throughput, so hide latency with computation not cache. + +So this is single instruction multiple thread (SIMT). + +**Thread Life Cycle**: + +Grid is launched on the SPA and thread blocks are serially distributed to all the SM. + +![image-20250424193125125](./hpc-2025-cuda/image-20250424193125125.webp) + +**SIMT Thread Execution**: + +Groups of 32 threads formed into warps. Threads in the same wraps always executing same instructions. And some threads may become inactive when code path diverges so the hardware **automatically Handles divergence**. + +Warps are the primitive unit of scheduling. + +> SIMT execution is an implementation choice. As sharing control logic leaves more space for ALUs. + +**SM Warp Scheduling**: + +SM hardware implements zero-overhead warp scheduling: + +- Warps whose next instruction has its operands ready for consumption are eligible for execution. +- Eligible warps are selected for execution on a prioritized scheduling policy. + +> If 4 clock cycles needed to dispatch the same instructions for all threads in a warp, and one global memory access is needed for every 4 instructions and memory latency is 200 cycles. So there should be 200 / (4 * 4) =12.5 (13) warps to fully tolerate the memory latency + +The SM warp scheduling use scoreboard and similar things. + +**Granularity Consideration**: + +Consider that int the G80 GPU, one SM can run 768 threads and 8 thread blocks, which is the best tiles to matrix multiplication: 16 * 16 = 256 and in one SM there can be 3 thread block which fully use the threads. + +### 2008: GT200 Architecture + +![image-20250424195111341](./hpc-2025-cuda/image-20250424195111341.webp) + +### 2010: Fermi GF100 GPU + +**Fermi SM**: + +![image-20250424195221886](./hpc-2025-cuda/image-20250424195221886.webp) + +There are 32 cores per SM and 512 cores in total, and introduce 64KB configureable L1/ shared memory. + +Decouple internal execution resource and dual issue pipelines to select two warps. + +And in Fermi, the debut the Parallel Thread eXecution(PTX) 2.0 ISA. + +### 2012 Kepler GK 110 + +![image-20250424200022880](./hpc-2025-cuda/image-20250424200022880.webp) + +### 2014 Maxwell + +4 GPCs and 16 SMM. + +![image-20250424200330783](./hpc-2025-cuda/image-20250424200330783.webp) + +### 2016 Pascal + +No thing to pay attention to. + +### 2017 Volta + +First introduce the tensor core, which is the ASIC to calculate matrix multiplication. + +### 2021 Ampere + +The GA100 SM: + +![image-20250508183446257](./hpc-2025-cuda/image-20250508183446257.webp) + +### 2022 Hopper + +Introduce the GH200 Grace Hopper Superchip: + +![image-20250508183528381](./hpc-2025-cuda/image-20250508183528381.webp) + +A system contains a CPU and GPU which is linked by a NVLink technology. + +And this system can scale out for machine learning. + +![image-20250508183724162](./hpc-2025-cuda/image-20250508183724162.webp) + +Memory access across the NVLink: + +- GPU to local CPU +- GPU to peer GPU +- GPU to peer CPU + +![image-20250508183931464](./hpc-2025-cuda/image-20250508183931464.webp) + +These operations can be handled by hardware accelerated memory coherency. Previously, there are separate page table for CPU and GPU but for GPU to access memory in both CPU and GPU, CPU and GPU can use the same page table. + +![image-20250508184155087](./hpc-2025-cuda/image-20250508184155087.webp) + +### 2025 Blackwell + +![image-20250508184455215](./hpc-2025-cuda/image-20250508184455215.webp) + +### Compute Capability + +The software version to show hardware version features and specifications. + + ## G80 Memory Hierarchy + +### Memory Space + +Each thread can + +- Read and write per-thread registers. +- Read and write per-thread local memory. +- Read and write pre-block shared memory. +- Read and write pre-grid global memory. +- Read only pre-grid constant memory. +- Read only pre-grid texture memory. + +![image-20250508185236920](./hpc-2025-cuda/image-20250508185236920.webp) + +Parallel Memory Sharing: + +- Local memory is per-thread and mainly for auto variables and register spill. +- Share memory is pre-block which can be used for inter thread communication. +- Global memory is pre-application which can be used for inter grid communication. + +### SM Memory Architecture + +![image-20250508185812302](./hpc-2025-cuda/image-20250508185812302.webp) + +Threads in a block share data and results in memory and shared memory. + +Shared memory is dynamically allocated to blocks which is one of the limiting resources. + +### SM Register File + +Register File(RF): there are 32KB, or 8192 entries, register for each SM in G80 GPU. + +The tex pipeline and local/store pipeline can read and write register file. + +Registers are dynamically partitioned across all blocks assigned to the SM. Once assigned to a block the register is **not** accessible by threads in other blocks and each thread in the same block only access registers assigned to itself. + +For a matrix multiplication example: + +- If one thread uses 10 registers and one block has 16x16 threads, each SM can contains three thread blocks as one thread blocks need 16 * 16 * 10 =2,560 registers and 3 * 2560 < 8192. +- But if each thread need 11 registers, one SM can only contains two blocks once as 8192 < 2816 * 3. + +More on dynamic partitioning: dynamic partitioning gives more flexibility to compilers and programmers. + +1. A smaller number of threads that require many registers each. +2. A large number of threads that require few registers each. + +So there is a tradeoff between instruction level parallelism and thread level parallelism. + +### Parallel Memory Architecture + +In a parallel machine, many threads access memory. So memory is divided into banks to achieve high bandwidth. + +Each bank can service one address per cycle. If multiple simultaneous accesses to a bank result in a bank conflict. + +Shared memory bank conflicts: + +- The fast cases: + - All threads of a half-warp access different banks, there's no back conflict. + - All threads of a half-warp access the identical address ,there is no bank conflict (by broadcasting). +- The slow cases: + - Multiple threads in the same half-warp access the same bank + +## Memory in Later Generations + +### Fermi Architecture + +**Unified Addressing Model** allows local, shared and global memory access using the same address space. + +![image-20250508193756274](./hpc-2025-cuda/image-20250508193756274.webp) + +**Configurable Caches** allows programmers to configure the size if L1 cache and the shared memory. + +The L1 cache works as a counterpart to shared memory: + +- Shared memory improves memory access for algorithms with well defined memory access. +- L1 cache improves memory access for irregular algorithms where data addresses are not known before hand. + +### Pascal Architecture + +**High Bandwidth Memory**: a technology which enables multiple layers of DRAM components to be integrated vertically on the package along with the GPU. + +![image-20250508194350572](./hpc-2025-cuda/image-20250508194350572.webp) + +**Unified Memory** provides a single and unified virtual address space for accessing all CPU and GPU memory in the system. + +And the CUDA system software doesn't need to synchronize all managed memory allocations to the GPU before each kernel launch. This is enabled by **memory page faulting**. + +## Advanced GPU Features + +### GigaThread + +Enable concurrent kernel execution: + +![image-20250508195840957](./hpc-2025-cuda/image-20250508195840957.webp) + +And provides dual **Streaming Data Transfer** engines to enable streaming data transfer, a.k.a direct memory access. + +![image-20250508195938546](./hpc-2025-cuda/image-20250508195938546.webp) + +### GPUDirect + +![image-20250508200041910](./hpc-2025-cuda/image-20250508200041910.webp) + +### GPU Boost + +GPU Boost works through real time hardware monitoring as opposed to application based profiles. It attempts to find what is the appropriate GPU frequency and voltage for a given moment in time. + +### SMX Architectural Details + +Each unit contains four warp schedulers. + +Scheduling functions: + +- Register scoreboard for long latency operations. +- Inter-warp scheduling decisions. +- Thread block level scheduling. + +### Improving Programmability + +![image-20250515183524043](./hpc-2025-cuda/image-20250515183524043.webp) + +**Dynamic Parallelism**: The ability to launch new grids from the GPU. + +And then introduce data-dependent parallelism and dynamic work generation and even batched and nested parallelism. + +The cpu controlled work batching: + +- CPU program limited by single point of control. +- Can run at most 10s of threads. +- CPU is fully consumed with controlling launches. + +![](./hpc-2025-cuda/image-20250515184225475.webp) + +Batching via dynamic parallelism: + +- Move top-level loops to GPUs. +- Run thousands of independent tasks. +- Release CPU for other work. + +![image-20250515184621914](./hpc-2025-cuda/image-20250515184621914.webp) + +### Grid Management Unit + +![image-20250515184714663](./hpc-2025-cuda/image-20250515184714663.webp) + +Fermi Concurrency: + +- Up to 16 grids can run at once. +- But CUDA streams multiplex into a single queue. +- Overlap only at stream edge. + +Kepler Improved Concurrency: + +- Up to 32 grids can run at once. +- One work queue per stream. +- Concurrency at full-stream level. +- No inter-stream dependencies. + +It is called as **Hyper-Q**. + +Without Hyper-Q: + +![image-20250515185019590](./hpc-2025-cuda/image-20250515185019590.webp) + +With Hyper-Q: + +![image-20250515185034758](./hpc-2025-cuda/image-20250515185034758.webp) + +In pascal, **asynchronous concurrent computing** is introduced. + +![image-20250515185801775](./hpc-2025-cuda/image-20250515185801775.webp) + +### NVLink: High-Speed Node Network + +![image-20250515185212184](./hpc-2025-cuda/image-20250515185212184.webp) + +> The *consumer* prefix means the product is designed for gamers. +> +> The *big* prefix means the product is designed for HPC. + +### Preemption + +Pascal can actually preempt at the lowest level, the instruction level. + +![image-20250515190244112](./hpc-2025-cuda/image-20250515190244112.webp) + +### Tensor Core + +Operates on a 4x4 matrix and performs: D = A x B + C. + +![image-20250515190507199](./hpc-2025-cuda/image-20250515190507199.webp) + +### GPU Multi-Process Scheduling + +- Timeslice scheduling: single process throughput optimization. +- Multi process service: multi-process throughput optimization. + +How about multi-process time slicing: + +![image-20250515190703918](./hpc-2025-cuda/image-20250515190703918.webp) + +Volta introduces the multi-process services: + +![image-20250515191142384](./hpc-2025-cuda/image-20250515191142384.webp) + + diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250424192311202.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424192311202.webp new file mode 100644 index 0000000..e96dddc --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424192311202.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f0e111666fbf2c927e4852cdbb4ec6e06ba440351f6ffd3e67413e0f3963ee0 +size 15530 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250424192825010.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424192825010.webp new file mode 100644 index 0000000..257bc3e --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424192825010.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eda9be9773e53b9c1b19539c753176a421a4705fcdfb47001c0467d85a9239f2 +size 15208 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250424193125125.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424193125125.webp new file mode 100644 index 0000000..5a6b1ca --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424193125125.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c307955cd05a6eeddd6aef30b57538775750e178417856d0c2ec973630e935fe +size 17696 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250424195111341.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424195111341.webp new file mode 100644 index 0000000..d71fbd2 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424195111341.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a633737fc0b834fad87818ee98c2e14d4dd42e2e0645a74876f9871d48b47168 +size 68264 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250424195221886.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424195221886.webp new file mode 100644 index 0000000..4d4cd81 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424195221886.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f24b6c69676ade40134c548e8025b5dacd7c9225c7dbfede5a2b9afb3834107e +size 60158 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250424200022880.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424200022880.webp new file mode 100644 index 0000000..aaf8943 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424200022880.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65332083c59ea8e39c1e59822c59bf3050d24cd1e6794774633e31bff5eadcb5 +size 79318 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250424200330783.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424200330783.webp new file mode 100644 index 0000000..161f56e --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250424200330783.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bb9274d58e632f46ad8be3321641e4cdbaa4165c3388df8de2d98a0b5b22b3a +size 46682 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183446257.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183446257.webp new file mode 100644 index 0000000..b0251d6 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183446257.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86611fcd51660121820ccdfab5a1db18d412fee98f2029deb8c5109bfa4178dc +size 59112 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183528381.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183528381.webp new file mode 100644 index 0000000..f89a78e --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183528381.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e8e8046354b1637dae15d96760ab20587e0c30104958349a928ab7b2901b94e +size 32956 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183724162.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183724162.webp new file mode 100644 index 0000000..222b37e --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183724162.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8ad0bb00f8b9da48e12bdb99f6ce58d810bed9cf72d09f53cf771f1dde04405 +size 66400 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183931464.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183931464.webp new file mode 100644 index 0000000..b3ee864 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508183931464.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea7d38b9dcb767203389c4a8e18315e396bb9f899ed7f923520569967b7ea4bf +size 46150 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508184155087.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508184155087.webp new file mode 100644 index 0000000..f9f0125 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508184155087.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40d6fd3963735b8236bbafeec9d58f20ad34057382b9a9689b5dfd188035729a +size 55910 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508184455215.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508184455215.webp new file mode 100644 index 0000000..8385304 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508184455215.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9bcabe4f1f40cbe4023bb898d94fdc85e0691cc7de6b778bb9057f6390d8797 +size 26942 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508185236920.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508185236920.webp new file mode 100644 index 0000000..f39560e --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508185236920.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f89b908ab76ce543c6f6074abbe561a2eae08f4c371f812aa68750920c73e7df +size 16338 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508185812302.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508185812302.webp new file mode 100644 index 0000000..79de09e --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508185812302.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7302d54d63c65c795ebbe8aa7cdf89d18a5c2e435e4aa2a63b9b78960639a814 +size 13960 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508193756274.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508193756274.webp new file mode 100644 index 0000000..ab6c288 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508193756274.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3eecac7e1d084ebd42bdc3272f171cc5ff6a05f01a8038b094165a851ff8dff3 +size 14376 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508194350572.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508194350572.webp new file mode 100644 index 0000000..48fa5a2 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508194350572.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a345cea108f141e30e381088eee11877785dde04720f515d95f002547c452ba1 +size 21504 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508195840957.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508195840957.webp new file mode 100644 index 0000000..c948d0a --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508195840957.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66a6fce8fd76e4c1dfc422d9e52096f8264d77f733269b291d0e24e24a4b0daf +size 13222 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508195938546.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508195938546.webp new file mode 100644 index 0000000..361a803 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508195938546.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:312eac09b66a185f073f88e3b9f4b8e04915ee3d69beedebf4894af00787bb69 +size 53104 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250508200041910.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508200041910.webp new file mode 100644 index 0000000..ca1086f --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250508200041910.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:285048b87012a358a7502f7c8c40205824025c2781e36f75ccef6920f1fdf785 +size 24732 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515183524043.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515183524043.webp new file mode 100644 index 0000000..89bc426 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515183524043.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65d20b3460822b5a5e70e6227738bac2378e56ce0d40bf6ed62bd53857a69266 +size 24048 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515184225475.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515184225475.webp new file mode 100644 index 0000000..8d0d501 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515184225475.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9da449b1ac9b8087c76f6ca2bc3e23c4186c7cfcb887d115c6e99b5584f5a287 +size 44426 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515184621914.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515184621914.webp new file mode 100644 index 0000000..180c816 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515184621914.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f77d1908ca8987bb746b670301a51db17f114cbad83b0ca69e85cde18a707d62 +size 28244 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515184714663.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515184714663.webp new file mode 100644 index 0000000..55f5b74 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515184714663.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb34ca7d6d92dd9e22ae942c8ada1614fd8e25b8095d8ec8307a09082967d76a +size 40020 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185019590.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185019590.webp new file mode 100644 index 0000000..9001517 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185019590.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ced6350d6187f5b38cd94523f553d7fdeb25d32a3722569f65a4ac2a7da1ba7 +size 14958 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185034758.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185034758.webp new file mode 100644 index 0000000..3c19092 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185034758.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3a6f1951815e0b76f898cf0c1d67cf011c0b32c5b96830e1de0a857606a92f0 +size 16056 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185212184.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185212184.webp new file mode 100644 index 0000000..a9bbc90 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185212184.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82ef79a681c49dccc42785b80aaae21e6032b325b06cc2a7ae0622fe1d8ffc99 +size 32912 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185801775.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185801775.webp new file mode 100644 index 0000000..840a2f9 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515185801775.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68ef3bea3c29a29504493127dc5daeb486d4e4f0b4067a7c6ea9c8115f1287d0 +size 46842 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515190244112.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515190244112.webp new file mode 100644 index 0000000..e333e43 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515190244112.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a11c38b980b05ff40e82fbdc1ed2caccd50fce7ad97433f6a8291f64932a8ab +size 20244 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515190507199.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515190507199.webp new file mode 100644 index 0000000..f840d49 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515190507199.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d791e759ea8a00679ba983319ac1e86db6247c9e9c5a32592570a8dcda1ddb2 +size 49348 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515190703918.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515190703918.webp new file mode 100644 index 0000000..09e2d9c --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515190703918.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6731401a0cd4fdb33cfbc783fcc870250134aa557cd311154af0f9fcf7a305c +size 47884 diff --git a/YaeBlog/source/posts/hpc-2025-cuda/image-20250515191142384.webp b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515191142384.webp new file mode 100644 index 0000000..5b88feb --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-cuda/image-20250515191142384.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10864b7445842aaf518ac0c0bd8bca0288dcc71deb22c9a4dfa3b92f6734b92c +size 31454 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing.md b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing.md new file mode 100644 index 0000000..e30a84c --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing.md @@ -0,0 +1,241 @@ +--- +title: High Performance Computing 25 SP Non Stored Program Computing +date: 2025-08-31T13:51:17.5260660+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + + + +No Von Neumann Machines. + + + +## Application Specified Integrated Circuits + +As known as **ASIC**, these hardwares can work along and are not von Neumann machines. + +No stored program concept: + +- Input data come in +- Pass through all circuit gates quickly +- Generate output results immediately + +Advantages: performance is better. + +Disadvantages: reusability is worse. + +> The CPU and GPU are special kinds of ASIC. + +Why we need ASIC in computing: + +- Alternatives to the Moore'a law. +- High capacity and high speed. + +![image-20250605185212740](./hpc-2025-non-stored-program-computing/image-20250605185212740.webp) + +### Full Custom ASICs + +All mask layers are customized in a full-custom ASICs. + +The full-custom ASICs always can offer the highest performance and lowest part cost (smallest die size) for a given design. + +A typical example of full-custom ASICs is the CPU. + +The advantages and disadvantages of full-custom ASICs is shown below. + +| Advantages | Disadvantages | +| ------------------------------------------------------------ | -------------------------------------------------------- | +| Reducing the area | The design process takes a longer time | +| Enhancing the performance | Having more complexity in computer-aided design tool | +| Better ability of integrating with other analog components and other pre-designed components | Requiring higher investment and skilled human resources. | + +### Semi Custom ASICs + +All the logical cell are predesigned and some or all of the mask layer is customized. + +There are two types of semi-custom ASICs: + +- Standard cell based ASICs +- Gate-array based ASICs. + +The Standard cell based ASICs is also called as **Cell-based ASIC(CBIC)**. + +![image-20250815093113115](./hpc-2025-non-stored-program-computing/image-20250815093113115.webp) + +> The *gate* is used a unit to measure the ability of semiconductor to store logical elements. + + The semi-custom ASICs is developed as: + +- Programmable Logic Array(PLA) +- Complex Programmable Logical Device(CPLD) +- Programmable Array Logical +- Field Programing Gate Array(FPGA) + +#### Programmable Logical Device + +An integrated circuit that can be programmed/reprogrammed with a digital logical of a curtain level. + +The basic idea of PLD is an array of **AND** gates and an array of **OR** gates. Each input feeds both a non-inverting buffer and an inverting buffer to produce the true and inverted forms of each variable. The AND outputs are called the product lines. Each product line is connected to one of the inputs of each OR gate. + +Depending on the structure, the standard PLD can be divided into: + +- Read Only Memory(ROM): A fixed array of AND gates and a programmable array of OR gates. +- Programmable Array Logic(PAL): A programmable array of AND gates feeding a fixed array of OR gates. +- Programmable Logic Array(PLA): A programmable array of AND gates feeding a programmable of OR gates. +- Complex Programmable Logic Device(CPLD) and Field Programmable Gate Array(FPGA): complex enough to be called as *architecture*. + +![image-20250817183832472](./hpc-2025-non-stored-program-computing/image-20250817183832472.webp) + + + +## Field Programming Gate Array + +> General speaking, all semiconductor can be considered as a special kind of ASIC. But in practice, we always refer the circuit with a special function as ASIC, a circuit that can change the function as FPGA. + +![image-20250612184120333](./hpc-2025-non-stored-program-computing/image-20250612184120333.webp) + +### FPGA Architecture + +![image-20250817184419856](./hpc-2025-non-stored-program-computing/image-20250817184419856.webp) + +#### Configurable Logic Block(CLB) Architecture + +The CLB consists of: + +- Look-up Table(LUT): implements the entries of a logic functions truth table. + + And some FPGAs can use the LUTs to implement small random access memory(RAM). + +- Carry and Control Logic: Implements fast arithmetic operation(adders/subtractors). + +- Memory Elements: configures flip flops/latches (programmable clock edges, set, reset and clock enable). These memory elements usually can be configured as shift-registers. + +##### Configuring LUTs + +LUT is a ram with data width of 1 bit and the content is programmed at power up. Internal signals connect to control signals of MUXs to select a values of the truth tables for any given input signals. + +The below figure shows LUT working: + +![image-20250817185111521](./hpc-2025-non-stored-program-computing/image-20250817185111521.webp) + +The configuration memory holds the output of truth table entries, so that when the FPGA is restarting it will run with the same *program*. + +And as the truth table entries are just bits, the program of FPGA is called as **BITSTREAM**, we download a bitstream to an FPGA and all LUTs will be configured using the BITSTREAM to implement the boolean logic. + +##### LUT Based Ram + +Let the input signal as address, the LUT will be configured as a RAM. Normally, LUT mode performs read operations, the address decoders can generate clock signal to latches for writing operation. + +![image-20250817185859510](./hpc-2025-non-stored-program-computing/image-20250817185859510.webp) + +#### Routing Architecture + +The logic blocks are connected to each though programmable routing network. And the routing network provides routing connections among logic blocks and I/O blocks to complete a user-designed circuit. + +Horizontal and vertical mesh or wire segments interconnection by programmable switches called programmable interconnect points(PIPs). + +![image-20250817192006784](./hpc-2025-non-stored-program-computing/image-20250817192006784.webp) + +These PIPs are implemented using a transmission gate controlled by a memory bits from the configuration memory. + +Several types of PIPs are used in the FPGA: + +- Cross-point: connects vertical or horizontal wire segments allowing turns. +- Breakpoint: connects or isolates 2 wire segments. +- Decoded MUX: groups of cross-points connected to a single output configured by n configuration bits. +- Non-decoded MUX: n wire segments each with a configuration bit. +- Compound cross-point: 6 breakpoint PIPs and can isolate two isolated signal nets. + +![image-20250817194355228](./hpc-2025-non-stored-program-computing/image-20250817194355228.webp) + +#### Input/Output Architecture + +The I/O pad and surrounding supporting logical and circuitry are referred as input/input cell. + +The programmable Input/Output cells consists of three parts: + +- Bi-directional buffers +- Routing resources. +- Programmable I/O voltage and current levels. + +![image-20250817195139631](./hpc-2025-non-stored-program-computing/image-20250817195139631.webp) + +#### Fine-grained and Coarse-grained Architecture + +The fine-grained architecture: + +- Each logic block can implement a very simple function. +- Very efficient in implementing systolic algorithms. +- Has a large number of interconnects per logic block than the functionality they offer. + +The coarse-grained architecture: + +- Each logic block is relatively packed with more logic. +- Has their logic blocks packed with more functionality. +- Has fewer interconnections which leading to reduce the propagating delays encountered. + +#### Interconnect Devices + +FPGAs are based on an array of logic modules and uncommitted wires to route signal. + +Three types of interconnected devices have been commonly used to connect there wires: + +- Static random access memory (SRAM) based +- Anti-fuse based +- EEPROM based + +### FPGA Design Flow + +![image-20250817195714935](./hpc-2025-non-stored-program-computing/image-20250817195714935.webp) + +![image-20250817200350750](./hpc-2025-non-stored-program-computing/image-20250817200350750.webp) + +The FPGA configuration techniques contains: + +- Full configuration and read back. +- Partial re-configuration and read back. +- Compressed configuration. + +Based on the partially reconfiguration, the runtime reconfiguration is development. The area to be reconfigured is changed based on run-time. + +#### Hardware Description Languages(HDL) + +There are three languages targeting FPGAs: + +- VHDL: VHSIC Hardware Description Language. +- Verilog +- OpenCL + +The first two language are typical HDL: + +| Verilog | VHDL | +| -------------------------------------- | ------------------------------- | +| Has fixed data types. | Has abstract data types. | +| Relatively easy to learn. | Relatively difficult to learn. | +| Good gate level timing. | Poor level gate timing. | +| Interpreted constructs. | Compiled constructs. | +| Limited design reusability. | Good design reusability. | +| Doesn't support structure replication. | Supports structure replication. | +| Limited design management. | Good design management. | + +The OpenCL is not an traditional hardare description language. And OpenCL needs to turn the thread parallelism into hardware parallelism, called **pipeline parallelism**. + +The follow figure shows how the OpenCL-FPGA compiler turns an vector adding function into the circuit. + +![image-20250829210329225](./hpc-2025-non-stored-program-computing/image-20250829210329225.webp) + +The compiler generates three stages for this function: + +1. In the first stage, two loading units are used. +2. In the second stage, one adding unit is used. +3. In the third stage, one storing unit is used. + +Once cycle, the thread `N` is clocked in the first stage, loading values from the array meanwhile, the thread `N - 1` is in the second stage, adding values from the array and the thread `N - 2` is in the third stage, storing value into the target array. + +So different from the CPU and GPU, the OpenCL on the FPGA has two levels of parallelism: + +- Pipelining +- Replication of the kernels and having them run concurrently. + diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250605185212740.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250605185212740.webp new file mode 100644 index 0000000..f0aa943 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250605185212740.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4fd2c98d02423204fd81464f246cf444d16c4cf26d0f0e16c1f7ab21c20ed9f +size 21240 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250612184120333.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250612184120333.webp new file mode 100644 index 0000000..d2478c1 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250612184120333.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13292910d38e288fc4d48b9d88326629702e7a653d2d1aa735687dd73efcc58f +size 30194 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250815093113115.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250815093113115.webp new file mode 100644 index 0000000..e6096da --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250815093113115.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:319b14c97406e1136c65698092690ea5aaaf6c0daaffc3396b0c41c3503da48b +size 24062 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817183832472.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817183832472.webp new file mode 100644 index 0000000..6fd531a --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817183832472.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:132348b505996e94cb7e3a1ab7fc388a08d64935665a2d073c4dec49d813f88e +size 30534 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817184419856.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817184419856.webp new file mode 100644 index 0000000..64b25a2 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817184419856.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8241a93f41e2919248d94df2a14f339e51c2b198eb0ac64129bc9e2def0fadc +size 62198 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817185111521.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817185111521.webp new file mode 100644 index 0000000..513897c --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817185111521.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e823c7aec0f9eed9f782707333e60370e8672c9b061f0c07d1f34c9dbaf4fbfa +size 41530 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817185859510.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817185859510.webp new file mode 100644 index 0000000..8dc8b10 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817185859510.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ca33f447039cc7542bd184df73956852f751fce229e01706f97bf4e2ec65f14 +size 46126 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817192006784.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817192006784.webp new file mode 100644 index 0000000..bcdcd1d --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817192006784.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b283d276e446af57556649391c2cf086c3505f0c06dc13776e4753565c80ff1 +size 6702 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817194355228.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817194355228.webp new file mode 100644 index 0000000..3672414 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817194355228.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:918e8fa4b3b016a139a4f37582f8b746dfb24957c273c3b03d0cc6af53b5c489 +size 29318 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817195139631.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817195139631.webp new file mode 100644 index 0000000..184753b --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817195139631.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08d76fc1e32f6be770a488954d603c2569c0da3d9e24707d24542bce84273efe +size 43880 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817195714935.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817195714935.webp new file mode 100644 index 0000000..240b423 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817195714935.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d89201be9c596d39611487cc9a959355ebd9e8d899c343aea7d3ac83f3041013 +size 22906 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817200350750.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817200350750.webp new file mode 100644 index 0000000..f3d4cbf --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250817200350750.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd110fe95490a1a8b59bf739a8502039268ba48eaf9e03c83f8b5174903d0fc3 +size 62148 diff --git a/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250829210329225.webp b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250829210329225.webp new file mode 100644 index 0000000..a008953 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-non-stored-program-computing/image-20250829210329225.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9600d963770da35ec737ec963ec300143a8ee160ddadc29500f4192712e1a97 +size 35924 diff --git a/YaeBlog/source/posts/hpc-2025-opencl.md b/YaeBlog/source/posts/hpc-2025-opencl.md new file mode 100644 index 0000000..b87a0f9 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-opencl.md @@ -0,0 +1,100 @@ +--- +title: High Performance Computing 25 SP OpenCL Programming +date: 2025-08-31T13:51:02.0181970+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + + +Open Computing Language. + + + +OpenCL is Open Computing Language. + +- Open, royalty-free standard C-language extension. +- For parallel programming of heterogeneous systems using GPUs, CPUs , CBE, DSP and other processors including embedded mobile devices. +- Managed by Khronos Group. + +![image-20250529185915068](./hpc-2025-opencl/image-20250529185915068.webp) + +### Anatomy of OpenCL + +- Platform Layer APi +- Runtime Api +- Language Specification + +### Compilation Model + +OpenCL uses dynamic/runtime compilation model like OpenGL. + +1. The code is compiled to an IR. +2. The IR is compiled to a machine code for execution. + +And in dynamic compilation, *step 1* is done usually once and the IR is stored. The app loads the IR and performs *step 2* during the app runtime. + +### Execution Model + +OpenCL program is divided into + +- Kernel: basic unit of executable code. +- Host: collection of compute kernels and internal functions. + +The host program invokes a kernel over an index space called an **NDRange**. + +NDRange is *N-Dimensional Range*, and can be a 1, 2, 3-dimensional space. + +A single kernel instance at a point of this index space is called **work item**. Work items are further grouped into **work groups**. + +### OpenCL Memory Model + +![image-20250529191215424](./hpc-2025-opencl/image-20250529191215424.webp) + +Multiple distinct address spaces: Address can be collapsed depending on the device's memory subsystem. + +Address space: + +- Private: private to a work item. +- Local: local to a work group. +- Global: accessible by all work items in all work groups. +- Constant: read only global memory. + +> Comparison with CUDA: +> +> ![image-20250529191414250](./hpc-2025-opencl/image-20250529191414250.webp) + +Memory region for host and kernel: + +![image-20250529191512490](./hpc-2025-opencl/image-20250529191512490.webp) + +### Programming Model + +#### Data Parallel Programming Model + +1. Define N-Dimensional computation domain +2. Work-items can be grouped together as *work group*. +3. Execute multiple work-groups in parallel. + +#### Task Parallel Programming Model + +> Data parallel execution model must be implemented by all OpenCL computing devices, but task parallel programming is a choice for vendor. + +Some computing devices such as CPUs can also execute task-parallel computing kernels. + +- Executes as s single work item. +- A computing kernel written in OpenCL. +- A native function. + +### OpenCL Framework + +![image-20250529192022613](./hpc-2025-opencl/image-20250529192022613.webp) + +The basic OpenCL program structure: + +![image-20250529192056388](./hpc-2025-opencl/image-20250529192056388.webp) + +**Contexts** are used to contain the manage the state of the *world*. + +**Command-queue** coordinates execution of the kernels. + diff --git a/YaeBlog/source/posts/hpc-2025-opencl/image-20250529185915068.webp b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529185915068.webp new file mode 100644 index 0000000..dc2df0b --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529185915068.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d62a7e597cdc80ca23a85623c9ba3634d17bdc7b098a78e71860653e5d0e370 +size 21306 diff --git a/YaeBlog/source/posts/hpc-2025-opencl/image-20250529191215424.webp b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529191215424.webp new file mode 100644 index 0000000..f0f66ad --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529191215424.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:621b4991c6cdcd881e92c43c919aaefcd6f7f5c0c3b97f7dc12e4e88a17128fa +size 22990 diff --git a/YaeBlog/source/posts/hpc-2025-opencl/image-20250529191414250.webp b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529191414250.webp new file mode 100644 index 0000000..f02a710 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529191414250.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7cf803398b2e38f84f4f1f7dd9dba6993d04e78f15b66688c0f1933e7c750394 +size 38110 diff --git a/YaeBlog/source/posts/hpc-2025-opencl/image-20250529191512490.webp b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529191512490.webp new file mode 100644 index 0000000..bc0dda0 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529191512490.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f7e34696ea72527d3777e96421a86167a9aec2b67aee55e381286994b977d1e +size 54722 diff --git a/YaeBlog/source/posts/hpc-2025-opencl/image-20250529192022613.webp b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529192022613.webp new file mode 100644 index 0000000..2f16196 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529192022613.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:62ddaa56863cb8ce5639252c238b18109b273c5bda0cf8d036e23875037a906a +size 31330 diff --git a/YaeBlog/source/posts/hpc-2025-opencl/image-20250529192056388.webp b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529192056388.webp new file mode 100644 index 0000000..45d0f9e --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-opencl/image-20250529192056388.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1072d33cce9d615be20bd7010c93b772472ef2d50da1e4406107b4e9dabeb031 +size 19236 diff --git a/YaeBlog/source/posts/hpc-2025-potpourri.md b/YaeBlog/source/posts/hpc-2025-potpourri.md new file mode 100644 index 0000000..17974d2 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-potpourri.md @@ -0,0 +1,79 @@ +--- +title: High Performance Computing 25 SP Potpourri +date: 2025-08-31T13:51:29.8809980+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + + +Potpourri has a good taste. + + + +## Heterogeneous System Architecture + +![image-20250612185019968](./hpc-2025-potpourri/image-20250612185019968.webp) + +The goals of the HSA: + +- Enable power efficient performance. +- Improve programmability of heterogeneous processors. +- Increase the portability of code across processors and platforms. +- Increase the pervasiveness of heterogeneous solutions. + +### The Runtime Stack + +![image-20250612185221643](./hpc-2025-potpourri/image-20250612185221643.webp) + +## Accelerated Processing Unit + +A processor that combines the CPU and the GPU elements into a single architecture. + +![image-20250612185743675](./hpc-2025-potpourri/image-20250612185743675.webp) + +## Intel Xeon Phi + +The goal: + +- Leverage X86 architecture and existing X86 programming models. +- Dedicate much of the silicon to floating point ops. +- Cache coherent. +- Increase floating-point throughput. +- Strip expensive features. + +The reality: + +- 10s of x86-based cores. +- Very high-bandwidth local GDDR5 memory. +- The card runs a modified embedded Linux. + +## Deep Learning: Deep Neural Networks + +The network can used as a computer. + +## Tensor Processing Unit + +A custom ASIC for the phase of Neural Networks (AI accelerator). + +### TPUv1 Architecture + +![image-20250612191035632](./hpc-2025-potpourri/image-20250612191035632.webp) + +### TPUv2 Architecture + +![image-20250612191118473](./hpc-2025-potpourri/image-20250612191118473.webp) + +Advantages of TPU: + +- Allows to make predications very quickly and respond within fraction of a second. +- Accelerate performance of linear computation, key of machine learning applications. +- Minimize the time to accuracy when you train large and complex network models. + +Disadvantages of TPU: + +- Linear algebra that requires heavy branching or are not computed on the basis of element wise algebra. +- Non-dominated matrix multiplication is not likely to perform well on TPUs. +- Workloads that access memory using sparse technique. +- Workloads that use highly precise arithmetic operations. + diff --git a/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185019968.webp b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185019968.webp new file mode 100644 index 0000000..c4386d4 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185019968.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d85a51185cf7b9cb9fb45e59f4024b38e04cc148046ad32a0a0bf44e347ac4f4 +size 22410 diff --git a/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185221643.webp b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185221643.webp new file mode 100644 index 0000000..e8b18cd --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185221643.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3cc99eda739d7546257bff4564c43320381c79d8c842ff239cf044394e065448 +size 16240 diff --git a/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185743675.webp b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185743675.webp new file mode 100644 index 0000000..7233d59 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612185743675.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bfe9c597fda41f26b68a6ce09bb8e14fd303ee4194d83725ecdffed9402a8ab +size 77656 diff --git a/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612191035632.webp b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612191035632.webp new file mode 100644 index 0000000..ebe8616 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612191035632.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ec5968a1677c98328ac58413baabcd5ff5352e7e9f09bae8ace8499fb369c57 +size 42574 diff --git a/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612191118473.webp b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612191118473.webp new file mode 100644 index 0000000..8aaf734 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-potpourri/image-20250612191118473.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df7c663316c7df33aab6fbfe738b69d4f7bb6e4e61cf91a1896d650b926678ce +size 18820 diff --git a/YaeBlog/source/posts/hpc-2025-program-cuda.md b/YaeBlog/source/posts/hpc-2025-program-cuda.md new file mode 100644 index 0000000..e4ff6b7 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-program-cuda.md @@ -0,0 +1,42 @@ +--- +title: High Performance Computing 25 SP Programming CUDA +date: 2025-08-31T13:50:53.6891520+08:00 +tags: +- 高性能计算 +- 学习资料 +--- + + +Compute Unified Device Architecture + + + +## CUDA + +General purpose programming model: + +- Use kicks off batches of threads on the GPU. + +![image-20250515195739382](./hpc-2025-program-cuda/image-20250515195739382.webp) + +The compiling C with CUDA applications: + +![image-20250515195907764](./hpc-2025-program-cuda/image-20250515195907764.webp) + +### CUDA APIs + +Areas: + +- Device management +- Context management +- Memory management +- Code module management +- Execution control +- Texture reference management +- Interoperability with OpenGL and Direct3D + +Two APIs: + +- A low-level API called the CUDA driver API. +- A higher-level API called the C runtime for CUDA that is implemented on top of the CUDA driver API. + diff --git a/YaeBlog/source/posts/hpc-2025-program-cuda/image-20250515195739382.webp b/YaeBlog/source/posts/hpc-2025-program-cuda/image-20250515195739382.webp new file mode 100644 index 0000000..ff5eaa3 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-program-cuda/image-20250515195739382.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6262a7c1a9bd6e60c823a20149f1c3d5885ccf77ed09de432cd6406bb03efaef +size 27260 diff --git a/YaeBlog/source/posts/hpc-2025-program-cuda/image-20250515195907764.webp b/YaeBlog/source/posts/hpc-2025-program-cuda/image-20250515195907764.webp new file mode 100644 index 0000000..5af4243 --- /dev/null +++ b/YaeBlog/source/posts/hpc-2025-program-cuda/image-20250515195907764.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67ae24b125253e6e2d475cd0ffbe5dd66ef98c13e4da8d309d770b4e2e1d9bad +size 29910 From dab866f13a212745456d76901645cdfc1c7792cb Mon Sep 17 00:00:00 2001 From: jackfiled Date: Tue, 2 Sep 2025 21:13:46 +0800 Subject: [PATCH 24/41] fix: use the right secrets to login registry in build action. --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index aa2bfde..9ce9072 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -27,7 +27,7 @@ jobs: with: registry: registry.cn-beijing.aliyuncs.com username: 初冬的朝阳 - password: ${{ secrets.ALIYUN_PASSWORD }} + password: ${{ secrets.ALIYUN_CR_PASSWORD }} auth_file_path: /etc/containers/auth.json - name: Push docker image run: podman push registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest From d1ec3a51d11f5be0148eaeab558c6040dc4c9b22 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sun, 19 Oct 2025 16:37:16 +0800 Subject: [PATCH 25/41] feat: update build action to use tencent cloud container registry. --- .editorconfig | 3 ++ .gitea/workflows/build.yaml | 63 +++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/.editorconfig b/.editorconfig index fd67d38..968b3d5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,6 +15,9 @@ trim_trailing_whitespace = true [project.json] indent_size = 2 +[*.{yaml,yml}] +indent_size = 2 + # C# and Visual Basic files [*.{cs,vb}] charset = utf-8-bom diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 9ce9072..4c75b53 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -1,33 +1,36 @@ name: Build blog docker image on: - push: - branches: - - master + push: + branches: + - master jobs: - Build-Blog-Image: - runs-on: archlinux - steps: - - uses: https://mirrors.rrricardo.top/actions/checkout.git@v4 - name: Check out code - with: - lfs: true - - name: Build project - run: | - cd YaeBlog - dotnet publish - - name: Build docker image - run: | - cd YaeBlog - podman build . -t registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest --build-arg COMMIT_ID=$(git rev-parse --short=10 HEAD) - - name: Workaround to make sure podman login succeed - run: | - mkdir /root/.docker - - name: Login aliyun docker registry - uses: https://mirrors.rrricardo.top/actions/podman-login.git@v1 - with: - registry: registry.cn-beijing.aliyuncs.com - username: 初冬的朝阳 - password: ${{ secrets.ALIYUN_CR_PASSWORD }} - auth_file_path: /etc/containers/auth.json - - name: Push docker image - run: podman push registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest + Build-Blog-Image: + runs-on: archlinux + steps: + - name: Check out code. + uses: https://mirrors.rrricardo.top/actions/checkout.git@v4 + with: + lfs: true + - name: Build project. + run: | + cd YaeBlog + dotnet publish + - name: Build docker image. + run: | + proxy + podman pull mcr.microsoft.com/dotnet/aspnet:9.0 + unproxy + cd YaeBlog + podman build . -t ccr.ccs.tencentyun.com/jackfiled/blog --build-arg COMMIT_ID=$(git rev-parse --short=10 HEAD) + - name: Workaround to make sure podman-login working. + run: | + mkdir /root/.docker + - name: Login tencent cloud docker registry. + uses: https://mirrors.rrricardo.top/actions/podman-login.git@v1 + with: + registry: ccr.ccs.tencentyun.com + username: 100044380877 + password: ${{ secrets.TENCENT_REGISTRY_PASSWORD }} + auth_file_path: /etc/containers/auth.json + - name: Push docker image. + run: podman push ccr.ccs.tencentyun.com/jackfiled/blog:latest From 009e86b5536649ff54b5cd8fb985d655931601f4 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Wed, 22 Oct 2025 20:04:32 +0800 Subject: [PATCH 26/41] blog: DNS failed in Podman Container (#19) Signed-off-by: jackfiled Reviewed-on: https://git.rrricardo.top/jackfiled/YaeBlog/pulls/19 --- YaeBlog/source/posts/podman-dns-failed.md | 130 ++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 YaeBlog/source/posts/podman-dns-failed.md diff --git a/YaeBlog/source/posts/podman-dns-failed.md b/YaeBlog/source/posts/podman-dns-failed.md new file mode 100644 index 0000000..5b7c75c --- /dev/null +++ b/YaeBlog/source/posts/podman-dns-failed.md @@ -0,0 +1,130 @@ +--- +title: Podman容器中特定域名解析失败问题排查 +date: 2025-10-22T17:42:07.7738936+08:00 +tags: +- 技术笔记 +- Bug Collections +--- + +最难以置信的一集。 + + + +## 问题现象 + +在折腾我的Gitea CI/CD管线的时候我,我尝试进行一项重要的变更:在运行CI任务时,任务中的容器支持从Docker out of Docker(DoD)迁移到Podman inside Podman(PiP)。但是在执行该项迁移的过程中,我遇到了如下一个非常神秘的Bug。 + +在CI任务运行的过程中,我发现一些**特定的**、**稳定的**域名无法在任务容器中正常解析并请求。目前注意到的域名有如下两个: + +- dot.net +- rcj.bupt-hpc.cn + +在容器内使用`curl http://dot.net -vvvvi`时,日志中重复出现如下内容。 + +``` +10:35:43.261007 [0-x] == Info: [MULTI] [INIT] added to multi, mid=1, running=1, total=2 +10:35:43.261252 [0-x] == Info: [MULTI] [INIT] multi_wait(fds=1, timeout=0) tinternal=0 +10:35:43.261533 [0-x] == Info: [MULTI] [INIT] -> [SETUP] +10:35:43.261690 [0-x] == Info: [MULTI] [SETUP] -> [CONNECT] +10:35:43.261838 [0-x] == Info: [READ] client_reset, clear readers +10:35:43.262042 [0-0] == Info: [MULTI] [CONNECT] [CPOOL] added connection 0. The cache now contains 1 members +10:35:43.262315 [0-0] == Info: [DNS] init threaded resolve of rcj.bupt-hpc.cn:9090 +10:35:43.262597 [0-0] == Info: [DNS] resolve thread started for of rcj.bupt-hpc.cn:9090 +10:35:43.262854 [0-0] == Info: [MULTI] [CONNECT] [TIMEOUT] set ASYNC_NAME to expire in 1000ns +10:35:43.263081 [0-0] == Info: [MULTI] [CONNECT] -> [RESOLVING] +10:35:43.263254 [0-0] == Info: [MULTI] [RESOLVING] multi_wait pollset[fd=4 IN], timeouts=1 +10:35:43.263500 [0-0] == Info: [MULTI] [RESOLVING] [TIMEOUT] ASYNC_NAME expires in 355ns +10:35:43.263743 [0-0] == Info: [MULTI] [RESOLVING] multi_wait(fds=2, timeout=1) tinternal=1 +10:35:43.265041 [0-0] == Info: [DNS] Curl_resolv_check() -> 0, missing +10:35:43.265218 [0-0] == Info: [MULTI] [RESOLVING] multi_wait pollset[fd=4 IN], timeouts=1 +10:35:43.265497 [0-0] == Info: [MULTI] [RESOLVING] [TIMEOUT] ASYNC_NAME expires in 1545ns +10:35:43.265736 [0-0] == Info: [MULTI] [RESOLVING] multi_wait(fds=2, timeout=2) tinternal=2 +10:35:43.268052 [0-0] == Info: [DNS] Curl_resolv_check() -> 0, missing +10:35:43.268236 [0-0] == Info: [MULTI] [RESOLVING] multi_wait pollset[fd=4 IN], timeouts=1 +10:35:43.268471 [0-0] == Info: [MULTI] [RESOLVING] [TIMEOUT] ASYNC_NAME expires in 3582ns +10:35:43.268688 [0-0] == Info: [MULTI] [RESOLVING] multi_wait(fds=2, timeout=4) tinternal=4 +``` + +## 初步假设与排除过程 + +这个问题非常奇怪,在问题排查初期,我进行了如下的操作。 + +1. 首先单独测试容器中的域名解析,使用`dig rcj.bupt-hpc.cn`进行解析测试,解析的结果符合预期,使用的DNS服务器是当前的容器的Podman网关地址10.17.14.1。 +2. 独立启动容器,而不是使用CI管线启动的容器,使用`podman run --rm -it --privileged ccr.ccs.tencentyun.com/jackfiled/runner-base:latest bash -l`创建容器,对上述出现问题的两个域名上的服务进行请求,没有问题,排除镜像本身的问题。 + +此时已经束手无策了,遂尝试AI辅助进行排查。 + +## AI辅助下的问题排查 + +### dig工具和libc的解析机制之前存在差异 + +AI首先提到,上面测试的dig工具和其他应用使用的libc解析机制之间存在差异: + +- `dig` 是直接使用 DNS 协议向 DNS 服务器发查询请求(绕过系统的解析器)。 +- `curl`(以及大多数应用)依赖系统 C 库(如 glibc)的 `getaddrinfo()` 函数,它会读取 `/etc/nsswitch.conf` 和 `/etc/resolv.conf`,并可能使用 `systemd-resolved`、`nscd` 等服务。 + +于是需要使用`getent`工具测试libc库的DNS解析是否存在问题,测试发现解析结果正常。 + +### IPv6 优先导致超时或失败 + +AI还提到,如果系统尝试先用 IPv6 解析(AAAA 记录),但网络不支持 IPv6,可能导致 `curl` 卡住或失败,而 `dig` 默认查 A 记录。 + +于是在使用`curl`时手动指定使用的网络协议,的确当使用`curl -4 http://dot.net`时可以正常的完成请求。 + +虽然这一方法可以解决问题,但是其给出的理由似乎无法解释我遇到的问题,问题域名中的rcj.bupt-hpc.cn并没有任何的AAAA解析记录,执行 `getent ahosts rcj.bupt-hpc.cn` 仅返回 IPv4 地址,无 AAAA 记录,说明系统解析器未返回 IPv6 地址,`curl -4` 的成功不能用IPv6解释。 + +### DNS服务器 + +在尝试AI给出的几个建议之后,我也开始尝试修改容器内的DNS服务器,进行这个尝试是因为我发现,使用`podman run`启动容器时,容器内部使用的DNS服务器是10.3.9.4这一主机使用的DNS服务器,而在CI管线中启动的容器使用的DNS服务器是172.17.14.1这一容器网关。 + +于是在CI启动的容器中,我通过直接写入`/etc/resolv.conf`文件的方式修改DNS服务器为10.3.9.4,在修改之后,针对两个域名的网络请求就恢复了正常。 + +在将上述的新发现同步给AI之后,AI又给出了一些新的思路。 + +### c-ares 异步 DNS 解析器兼容性问题? + +- 某些编译版本的 `curl`(尤其是 Alpine、某些容器镜像)会链接 `c-ares` 库,**绕过 glibc 的 `getaddrinfo`**,直接读取 `/etc/resolv.conf` 并发 DNS 请求。 +- `c-ares` **不读取 `/etc/nsswitch.conf`**,也不支持 mDNS、systemd-resolved 等机制。 +- 如果 `/etc/resolv.conf` 中的 nameserver(如 `172.17.14.1`)对 **某些域名响应异常**(如超时、截断、不响应 UDP 包),`c-ares` 会卡住。 + +但是按照AI提供的步骤进行排查: + +- `curl --version` 输出中**未包含 `c-ares`**; +- curl 使用 glibc 的 `getaddrinfo()` 或 libcurl threaded resolver; +- **结论**:非 c-ares 导致。 + +### UDP DNS 响应截断(>512 字节)? + +- `c-ares` 默认使用 **UDP** 查询 DNS。 +- 如果 Podman 的内置 DNS 代理(通常是 `netavark` + `aardvark-dns` 或旧版 `dnsmasq`)在处理 **特定域名** 的 UDP 请求时: + - 响应过大导致 UDP 截断(TC=1),但 `c-ares` 未自动重试 TCP; + - 或代理本身 bug 导致丢包; +- 则 `c-ares` 会认为“无响应”,返回“missing”。 + +而 `dig` 默认使用 UDP,但**当 TC=1时会自动使用TCP重试**,所以成功,而你的域名 `rcj.bupt-hpc.cn` 可能有较多 CNAME 或 TXT 记录,导致 UDP 响应超 512 字节。 + +但是我清楚的知道域名 `rcj.bupt-hpc.cn` 仅有一条 A 记录,DNS 响应极小,`dig` 与 `getent` 均能快速返回结果。 + +## AI Conclusion:Podman 内置 DNS 代理(aardvark-dns)行为异常 + +Podman 4.5.2 默认使用 **aardvark-dns** 作为容器网络的 DNS 代理,监听于网关地址(如 `172.10.0.1`)。其职责包括: + +- 解析容器名称(服务发现); +- 转发外部域名查询至上层 DNS。 + +可能的故障点包括: + +1. aardvark-dns 对特定域名后缀(如 `.bupt-hpc.cn`)的转发逻辑存在缺陷; +2. 响应格式不符合 glibc 或 libcurl 的预期(如缺少 AA 标志、TTL 异常); +3. 首次查询存在延迟或丢包,而 libcurl threaded resolver 未充分重试; +4. 与宿主机 `/etc/resolv.conf` 中的 `search` 域交互异常,导致拼接错误查询。 + +尽管 `dig` 能获取结果,但 `dig` 具有更强的容错性和自动重试机制(如 TC=1 时切 TCP),而 libcurl 的 resolver 更严格。 + +> 对于AI的这个结果,说实话我表示怀疑,我感觉还是和IPv6有关系,这样才能同时解释`curl -4`和更换DNS都可以运行的现象。 + +## 临时缓解 + +最终,我在CI启动容器的配置选项中添加`--dns 10.3.9.4`,不使用Podman启动的DNS服务器,暂时绕过了这个问题。 + +不过这个问题并没有从本质上得到解决,甚至都还不知道背后的具体问题是什么,感觉会在后面攻击我,特此记录。 From 58ba4b2a2f95070000c2e25092012b75ae11f96c Mon Sep 17 00:00:00 2001 From: jackfiled Date: Wed, 22 Oct 2025 21:32:49 +0800 Subject: [PATCH 27/41] fix: not watching hidden files when triggering hot reload. Fix incorrect page count calculation. --- YaeBlog/Pages/BlogIndex.razor | 5 +++++ YaeBlog/Services/BlogHotReloadService.cs | 13 +++++++++++-- YaeBlog/Services/EssayScanService.cs | 10 ++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/YaeBlog/Pages/BlogIndex.razor b/YaeBlog/Pages/BlogIndex.razor index 7c8b683..c5766c5 100644 --- a/YaeBlog/Pages/BlogIndex.razor +++ b/YaeBlog/Pages/BlogIndex.razor @@ -39,6 +39,11 @@ { _page = Page ?? 1; _pageCount = Contents.Count / EssaysPerPage + 1; + (_pageCount, int reminder) = int.DivRem(Contents.Count, EssaysPerPage); + if (reminder > 0) + { + _pageCount += 1; + } if (EssaysPerPage * _page > Contents.Count + EssaysPerPage) { diff --git a/YaeBlog/Services/BlogHotReloadService.cs b/YaeBlog/Services/BlogHotReloadService.cs index 581c1b4..80d8c69 100644 --- a/YaeBlog/Services/BlogHotReloadService.cs +++ b/YaeBlog/Services/BlogHotReloadService.cs @@ -16,11 +16,11 @@ public sealed class BlogHotReloadService( await rendererService.RenderAsync(true); - Task[] reloadTasks = [FileWatchTask(stoppingToken)]; + Task[] reloadTasks = [WatchFileAsync(stoppingToken)]; await Task.WhenAll(reloadTasks); } - private async Task FileWatchTask(CancellationToken token) + private async Task WatchFileAsync(CancellationToken token) { while (!token.IsCancellationRequested) { @@ -33,6 +33,15 @@ public sealed class BlogHotReloadService( break; } + FileInfo changeFileInfo = new(changeFile); + + if (changeFileInfo.Name.StartsWith('.')) + { + // Ignore dot-started file and directory. + logger.LogDebug("Ignore hidden file: {}.", changeFile); + continue; + } + logger.LogInformation("{} changed, re-rendering.", changeFile); essayContentService.Clear(); await rendererService.RenderAsync(true); diff --git a/YaeBlog/Services/EssayScanService.cs b/YaeBlog/Services/EssayScanService.cs index be50ab6..09b1baa 100644 --- a/YaeBlog/Services/EssayScanService.cs +++ b/YaeBlog/Services/EssayScanService.cs @@ -109,6 +109,12 @@ public partial class EssayScanService : IEssayScanService { foreach (BlogResult blog in fileContents) { + if (blog.BlogContent.Length < 4) + { + // Even not contains a legal header. + continue; + } + int endPos = blog.BlogContent.IndexOf("---", 4, StringComparison.Ordinal); if (!blog.BlogContent.StartsWith("---") || endPos is -1 or 0) { @@ -121,14 +127,14 @@ public partial class EssayScanService : IEssayScanService try { MarkdownMetadata metadata = _yamlDeserializer.Deserialize(metadataString); - _logger.LogDebug("Scan metadata title: '{}' for {}.", metadata.Title, blog.BlogFile.Name); + _logger.LogDebug("Scan metadata title: '{title}' for {name}.", metadata.Title, blog.BlogFile.Name); contents.Add(new BlogContent(blog.BlogFile, metadata, blog.BlogContent[(endPos + 3)..], isDraft, blog.Images, blog.NotFoundImages)); } catch (YamlException e) { - _logger.LogWarning("Failed to parser metadata from {} due to {}, skipping", blog.BlogFile.Name, e); + _logger.LogWarning("Failed to parser metadata from {name} due to {exception}, skipping", blog.BlogFile.Name, e); } } }); From 56374a4e6b7a777f23baaf68a1533a73e00d1fa8 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sun, 4 Jan 2026 01:04:00 +0800 Subject: [PATCH 28/41] feat: update to .net 10. (#20) Use ClientAssets to build tailwind styles. Make blog anchors not open new tab. Reviewed-on: https://git.rrricardo.top/jackfiled/YaeBlog/pulls/20 --- .gitea/workflows/build.yaml | 2 +- YaeBlog/Components/App.razor | 29 +++++++--- YaeBlog/Components/EssayCard.razor | 6 +-- YaeBlog/Components/Layout/BlogLayout.razor | 28 ++++++++++ .../{ => Components}/Layout/MainLayout.razor | 0 YaeBlog/{ => Components}/Pages/About.razor | 0 YaeBlog/{ => Components}/Pages/Archives.razor | 0 .../{ => Components}/Pages/BlogIndex.razor | 0 YaeBlog/{ => Components}/Pages/Essays.razor | 0 YaeBlog/{ => Components}/Pages/Friends.razor | 0 YaeBlog/{ => Components}/Pages/Index.razor | 2 +- YaeBlog/{ => Components}/Pages/NotFound.razor | 0 YaeBlog/{ => Components}/Pages/Tags.razor | 0 YaeBlog/{ => Components}/_Imports.razor | 1 - YaeBlog/Directory.Build.targets | 53 +++++++++++++++++++ YaeBlog/Dockerfile | 4 +- YaeBlog/Layout/BlogLayout.razor | 44 --------------- YaeBlog/YaeBlog.csproj | 27 ++-------- YaeBlog/appsettings.json | 2 +- YaeBlog/build-tailwind.ps1 | 7 +++ YaeBlog/docker-compose.yaml | 20 +++---- YaeBlog/wwwroot/globals.css | 15 ------ 22 files changed, 133 insertions(+), 107 deletions(-) create mode 100644 YaeBlog/Components/Layout/BlogLayout.razor rename YaeBlog/{ => Components}/Layout/MainLayout.razor (100%) rename YaeBlog/{ => Components}/Pages/About.razor (100%) rename YaeBlog/{ => Components}/Pages/Archives.razor (100%) rename YaeBlog/{ => Components}/Pages/BlogIndex.razor (100%) rename YaeBlog/{ => Components}/Pages/Essays.razor (100%) rename YaeBlog/{ => Components}/Pages/Friends.razor (100%) rename YaeBlog/{ => Components}/Pages/Index.razor (95%) rename YaeBlog/{ => Components}/Pages/NotFound.razor (100%) rename YaeBlog/{ => Components}/Pages/Tags.razor (100%) rename YaeBlog/{ => Components}/_Imports.razor (96%) create mode 100644 YaeBlog/Directory.Build.targets delete mode 100644 YaeBlog/Layout/BlogLayout.razor create mode 100644 YaeBlog/build-tailwind.ps1 delete mode 100644 YaeBlog/wwwroot/globals.css diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 4c75b53..ecc1cd8 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -18,7 +18,7 @@ jobs: - name: Build docker image. run: | proxy - podman pull mcr.microsoft.com/dotnet/aspnet:9.0 + podman pull mcr.microsoft.com/dotnet/aspnet:10.0 unproxy cd YaeBlog podman build . -t ccr.ccs.tencentyun.com/jackfiled/blog --build-arg COMMIT_ID=$(git rev-parse --short=10 HEAD) diff --git a/YaeBlog/Components/App.razor b/YaeBlog/Components/App.razor index 75b2658..8ff6304 100644 --- a/YaeBlog/Components/App.razor +++ b/YaeBlog/Components/App.razor @@ -5,16 +5,33 @@ - - - - + + + + + - - + + diff --git a/YaeBlog/Components/EssayCard.razor b/YaeBlog/Components/EssayCard.razor index 99cb3f1..955935f 100644 --- a/YaeBlog/Components/EssayCard.razor +++ b/YaeBlog/Components/EssayCard.razor @@ -3,7 +3,7 @@
    @@ -14,9 +14,7 @@ @foreach (string key in Essay.Tags) { }
    diff --git a/YaeBlog/Components/Layout/BlogLayout.razor b/YaeBlog/Components/Layout/BlogLayout.razor new file mode 100644 index 0000000..1eb1c78 --- /dev/null +++ b/YaeBlog/Components/Layout/BlogLayout.razor @@ -0,0 +1,28 @@ +@inherits LayoutComponentBase + +@attribute [StreamRendering] + +
    +
    + + +
    +
    + + + + +
    +
    +
    + +
    + @Body +
    + + +
    diff --git a/YaeBlog/Layout/MainLayout.razor b/YaeBlog/Components/Layout/MainLayout.razor similarity index 100% rename from YaeBlog/Layout/MainLayout.razor rename to YaeBlog/Components/Layout/MainLayout.razor diff --git a/YaeBlog/Pages/About.razor b/YaeBlog/Components/Pages/About.razor similarity index 100% rename from YaeBlog/Pages/About.razor rename to YaeBlog/Components/Pages/About.razor diff --git a/YaeBlog/Pages/Archives.razor b/YaeBlog/Components/Pages/Archives.razor similarity index 100% rename from YaeBlog/Pages/Archives.razor rename to YaeBlog/Components/Pages/Archives.razor diff --git a/YaeBlog/Pages/BlogIndex.razor b/YaeBlog/Components/Pages/BlogIndex.razor similarity index 100% rename from YaeBlog/Pages/BlogIndex.razor rename to YaeBlog/Components/Pages/BlogIndex.razor diff --git a/YaeBlog/Pages/Essays.razor b/YaeBlog/Components/Pages/Essays.razor similarity index 100% rename from YaeBlog/Pages/Essays.razor rename to YaeBlog/Components/Pages/Essays.razor diff --git a/YaeBlog/Pages/Friends.razor b/YaeBlog/Components/Pages/Friends.razor similarity index 100% rename from YaeBlog/Pages/Friends.razor rename to YaeBlog/Components/Pages/Friends.razor diff --git a/YaeBlog/Pages/Index.razor b/YaeBlog/Components/Pages/Index.razor similarity index 95% rename from YaeBlog/Pages/Index.razor rename to YaeBlog/Components/Pages/Index.razor index 1546c3a..dab799c 100644 --- a/YaeBlog/Pages/Index.razor +++ b/YaeBlog/Components/Pages/Index.razor @@ -7,7 +7,7 @@
    - Ricardo's Avatar + Ricardo's Avatar
    diff --git a/YaeBlog/Pages/NotFound.razor b/YaeBlog/Components/Pages/NotFound.razor similarity index 100% rename from YaeBlog/Pages/NotFound.razor rename to YaeBlog/Components/Pages/NotFound.razor diff --git a/YaeBlog/Pages/Tags.razor b/YaeBlog/Components/Pages/Tags.razor similarity index 100% rename from YaeBlog/Pages/Tags.razor rename to YaeBlog/Components/Pages/Tags.razor diff --git a/YaeBlog/_Imports.razor b/YaeBlog/Components/_Imports.razor similarity index 96% rename from YaeBlog/_Imports.razor rename to YaeBlog/Components/_Imports.razor index e5a1ddf..e1cbac4 100644 --- a/YaeBlog/_Imports.razor +++ b/YaeBlog/Components/_Imports.razor @@ -6,5 +6,4 @@ @using static Microsoft.AspNetCore.Components.Web.RenderMode @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop -@using YaeBlog @using YaeBlog.Components diff --git a/YaeBlog/Directory.Build.targets b/YaeBlog/Directory.Build.targets new file mode 100644 index 0000000..fd87244 --- /dev/null +++ b/YaeBlog/Directory.Build.targets @@ -0,0 +1,53 @@ + + + pnpm install + pnpm run build + --output + + + + <_RestoreClientAssetsBeforeTargets Condition="'$(TargetFramework)' == ''">DispatchToInnerBuilds + + + + + + + + + + <_ClientAssetsOutputFullPath>$([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)ClientAssets')) + + + + + + + <_ClientAssetsBuildOutput Include="$(IntermediateOutputPath)ClientAssets\**"/> + + + + + + + + + + + + + + + + + + diff --git a/YaeBlog/Dockerfile b/YaeBlog/Dockerfile index fc49c56..933638a 100644 --- a/YaeBlog/Dockerfile +++ b/YaeBlog/Dockerfile @@ -1,10 +1,10 @@ -FROM mcr.microsoft.com/dotnet/aspnet:9.0 +FROM mcr.microsoft.com/dotnet/aspnet:10.0 ARG COMMIT_ID ENV COMMIT_ID=${COMMIT_ID} WORKDIR /app -COPY bin/Release/net9.0/publish/ ./ +COPY bin/Release/net10.0/publish/ ./ COPY source/ ./source/ COPY appsettings.json . diff --git a/YaeBlog/Layout/BlogLayout.razor b/YaeBlog/Layout/BlogLayout.razor deleted file mode 100644 index 82ab91a..0000000 --- a/YaeBlog/Layout/BlogLayout.razor +++ /dev/null @@ -1,44 +0,0 @@ -@inherits LayoutComponentBase - -@attribute [StreamRendering] - -
    - - -
    - @Body -
    - - -
    diff --git a/YaeBlog/YaeBlog.csproj b/YaeBlog/YaeBlog.csproj index 29ba300..6a6d8a7 100644 --- a/YaeBlog/YaeBlog.csproj +++ b/YaeBlog/YaeBlog.csproj @@ -11,30 +11,13 @@ - net9.0 + net10.0 enable enable - - - - - - - - - - - - - - - - - - - - - + + pnpm install + pwsh build-tailwind.ps1 + diff --git a/YaeBlog/appsettings.json b/YaeBlog/appsettings.json index 27635cb..7f75dba 100644 --- a/YaeBlog/appsettings.json +++ b/YaeBlog/appsettings.json @@ -37,7 +37,7 @@ "Name": "万木长风", "Description": "世界渲染中...", "Link": "https://ryohai.fun", - "AvatarImage": "https://ryohai.fun/icon.jpg" + "AvatarImage": "https://ryohai.fun/static/favicons/favicon-32x32.png" } ] } diff --git a/YaeBlog/build-tailwind.ps1 b/YaeBlog/build-tailwind.ps1 new file mode 100644 index 0000000..cfb7e65 --- /dev/null +++ b/YaeBlog/build-tailwind.ps1 @@ -0,0 +1,7 @@ +[cmdletbinding()] +param( + [string]$output = "wwwroot" +) + +Write-Output "Output directory: $output" +pnpm tailwindcss -i wwwroot/tailwind.css -o $output/tailwind.g.css diff --git a/YaeBlog/docker-compose.yaml b/YaeBlog/docker-compose.yaml index a505b3d..bc985dd 100644 --- a/YaeBlog/docker-compose.yaml +++ b/YaeBlog/docker-compose.yaml @@ -1,13 +1,13 @@ version: '3.8' services: - blog: - image: registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest - restart: unless-stopped - labels: - - "traefik.enable=true" - - "traefik.http.routers.blog.rule=Host(`rrricardo.top`) || Host(`www.rrricardo.top`)" - - "traefik.http.services.blog.loadbalancer.server.port=8080" - - "traefik.http.routers.blog.tls=true" - - "traefik.http.routers.blog.tls.certresolver=myresolver" - - "com.centurylinklabs.watchtower.enable=true" + blog: + image: registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest + restart: unless-stopped + labels: + - "traefik.enable=true" + - "traefik.http.routers.blog.rule=Host(`rrricardo.top`) || Host(`www.rrricardo.top`)" + - "traefik.http.services.blog.loadbalancer.server.port=8080" + - "traefik.http.routers.blog.tls=true" + - "traefik.http.routers.blog.tls.certresolver=myresolver" + - "com.centurylinklabs.watchtower.enable=true" diff --git a/YaeBlog/wwwroot/globals.css b/YaeBlog/wwwroot/globals.css deleted file mode 100644 index ecbb546..0000000 --- a/YaeBlog/wwwroot/globals.css +++ /dev/null @@ -1,15 +0,0 @@ -@font-face { - font-family: "Font Awesome 6 Free"; - font-style: normal; - font-weight: 400; - font-display: block; - src: url(fonts/fa-regular-400.woff2) format("woff2"), url(fonts/fa-regular-400.ttf) format("truetype") -} - -@font-face { - font-family: "Font Awesome 6 Free"; - font-style: normal; - font-weight: 900; - font-display: block; - src: url(fonts/fa-solid-900.woff2) format("woff2"), url(fonts/fa-solid-900.ttf) format("truetype") -} From c050d1b790fa8f2ed8d67a1802a8d0d21275b175 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sun, 4 Jan 2026 17:21:52 +0800 Subject: [PATCH 29/41] feat: add build.ps1 script. --- .gitea/workflows/build.yaml | 6 +-- YaeBlog/YaeBlog.csproj | 2 +- YaeBlog/build-tailwind.ps1 | 7 --- YaeBlog/build.ps1 | 90 +++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 13 deletions(-) delete mode 100644 YaeBlog/build-tailwind.ps1 create mode 100755 YaeBlog/build.ps1 diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index ecc1cd8..fe5c0ab 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -12,16 +12,12 @@ jobs: with: lfs: true - name: Build project. - run: | - cd YaeBlog - dotnet publish - - name: Build docker image. run: | proxy podman pull mcr.microsoft.com/dotnet/aspnet:10.0 unproxy cd YaeBlog - podman build . -t ccr.ccs.tencentyun.com/jackfiled/blog --build-arg COMMIT_ID=$(git rev-parse --short=10 HEAD) + pwsh build.ps1 build - name: Workaround to make sure podman-login working. run: | mkdir /root/.docker diff --git a/YaeBlog/YaeBlog.csproj b/YaeBlog/YaeBlog.csproj index 6a6d8a7..642cf90 100644 --- a/YaeBlog/YaeBlog.csproj +++ b/YaeBlog/YaeBlog.csproj @@ -18,6 +18,6 @@ pnpm install - pwsh build-tailwind.ps1 + pwsh build.ps1 tailwind diff --git a/YaeBlog/build-tailwind.ps1 b/YaeBlog/build-tailwind.ps1 deleted file mode 100644 index cfb7e65..0000000 --- a/YaeBlog/build-tailwind.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -[cmdletbinding()] -param( - [string]$output = "wwwroot" -) - -Write-Output "Output directory: $output" -pnpm tailwindcss -i wwwroot/tailwind.css -o $output/tailwind.g.css diff --git a/YaeBlog/build.ps1 b/YaeBlog/build.ps1 new file mode 100755 index 0000000..0242036 --- /dev/null +++ b/YaeBlog/build.ps1 @@ -0,0 +1,90 @@ +#!pwsh + +[cmdletbinding()] +param( + [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Specify the build target")] + [ValidateSet("tailwind", "watch", "publish", "compress", "build")] + [string]$Target, + [string]$Output = "wwwroot", + [string]$Essay, + [switch]$Compress +) + +begin { + Write-Host "Building $Target..." + + if ($Target -eq "publish") + { + if ($Essay -eq "") + { + Write-Error "No publish target, please add with --essay argument." + exit 1 + } + } +} + +process { + function Compress-Image + { + Write-Host "Compress image assets..." + dotnet run -- compress --dry-run + $confirm = Read-Host "Really compress images? (y/n)" + if ($confirm -notmatch "^[yY]$") + { + Write-Host "Not compress images." + return + } + + Write-Host "Do compress image..." + dotnet run -- compress + + dotnet run -- scan + $confirm = Read-Host "Really delete unused images? (y/n)" + if ($confirm -notmatch "^[yY]$") + { + Write-Host "Not delete images." + return + } + Write-Host "Do delete unused images.." + dotnet run -- scan --rm + } + + function Build-Image + { + $commitId = git rev-parse --short=10 HEAD + dotnet publish + podman build . -t ccr.ccs.tencentyun.com/jackfiled/blog --build-arg COMMIT_ID=$commitId + } + + switch ($Target) + { + "tailwind" { + Write-Host "Build tailwind css into $Output." + pnpm tailwindcss -i wwwroot/tailwind.css -o $Output/tailwind.g.css + break + } + "watch" { + dotnet run -- watch + break + } + "publish" { + Write-Host "Publish essay $Essay..." + dotnet run -- publish $Essay + + if ($Compress) + { + Compress-Image + } + break + } + "compress" { + Compress-Image + break + } + "build" { + Build-Image + break + } + } +} + From 1be39327aa8d530ad5cb9244b98c86b9fa1f3b57 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sun, 4 Jan 2026 17:48:29 +0800 Subject: [PATCH 30/41] blog: ASP.NET Core static web assets blog. Signed-off-by: jackfiled --- YaeBlog/source/posts/aspnetcore-swa.md | 224 ++++++++++++++++++ .../image-20251231225433184.webp | 3 + 2 files changed, 227 insertions(+) create mode 100644 YaeBlog/source/posts/aspnetcore-swa.md create mode 100644 YaeBlog/source/posts/aspnetcore-swa/image-20251231225433184.webp diff --git a/YaeBlog/source/posts/aspnetcore-swa.md b/YaeBlog/source/posts/aspnetcore-swa.md new file mode 100644 index 0000000..6244f7b --- /dev/null +++ b/YaeBlog/source/posts/aspnetcore-swa.md @@ -0,0 +1,224 @@ +--- +title: ASP.NET Core中的静态Web资产 +date: 2026-01-04T16:36:36.5629759+08:00 +tags: +- 技术笔记 +- dotnet +- ASP.NET Core +--- + + +Web服务器应该如何扫描与提供静态Web文件,尤其是在考虑到缓存、压缩的情况下,还需要正确的处理开发环境和部署环境之间的差异?让我们来看看ASP.NET Core是如何处理这个问题的。以及如何将通过其他工具(例如`pnpm`)生成的前端资产文件集成到ASP.NET Core中。 + + + +### 引言——Blazor开发中的静态Web资源 + +Blazor是ASP.NET Core中~~新推出的~~Web应用程序开发框架,通过一系列精巧的设计实现了使用HTML和C#编写运行在浏览器中的应用程序,避免了使用丑陋的JavaScript。但是现代的前端开发生态几乎都建立在JavaScript之上,尤其是考虑在JavaScript在很长的一段时间都是浏览器唯一支持的脚本语言,在Blazor项目开发的过程中必然会遇到一些只能编写JavaScript才能解决的问题。同时,一系列的现代前端工具,例如[tailwindcss](https://tailwindcss.com/),提供了更加优秀的前端开发体验,但是这些都基于NodeJS和NPM等前端工具。以上的前端生态引入了一个问题,如何在MSBuild驱动的Blazor应用构建流程中自然地运行前端工具链和ASP.NET Core支持的服务器部署生成的静态资源? + +Blazor目前提供了一个入口简洁但是功能丰富的静态Web资源提供功能。在使用默认应用目录的情况下,项目将会提供一个`wwwroot`文件夹,这个文件夹中的内容将可以从`/`直接寻址。为了提升前端静态文件的使用体验,该文件夹下的文件将会经过一个复杂的管道: + +- 在构建扫描到这些资产文件之后,MSBuild将会给静态文件加上内容指纹,以防止重复使用旧文件。资源还会被压缩,以减少资产交付的时间。 +- 在运行时,所发现的资产文件将会作为终结点公开,并添加上合适的缓存头和内容类型头。在设置`ETtag`,`Last-Modified`和`Content-Type`头之后,浏览器将可以合理的缓存这些静态文件直到应用更新。 + +该静态文件功能还需要适应应用程序的部署状态: + +- 在开发时,或者说运行`dotnet run`和`dotnet build`时,该功能需要将对应的静态文件终结点URL映射到磁盘上存储的实际静态文件上,就像它们实际上就在`wwwroot`文件夹中一样。考虑到实际上开发过程中会用到Blazor内部的资产文件`blazor.web.js`,引用项目中的资产文件等等,这实际上一个相当复杂的检测-映射流程。 +- 在发布时,或者说运行`dotnet publish`时,该功能需要收集所有需要的静态文件并复制到最终发布文件夹的`wwwroot`文件夹之下。 + +### Microsoft.AspNetCore.ClientAssets + +在默认的应用模板下,如果需要使用其他的现代前端工具生成静态资产文件,最简单的方法就是手动或者编写MSBuild目标(Target)生成资产文件并放在`wwwroot`文件夹中。但是这个方法存在着如下几个问题: + +- 开发者需要编写 MSBuild 目标(targets)来调用他们的工具。 +- 开发者通常没有在构建过程的恰当时机执行其自定义目标。 +- 开发者将工具生成的输出文件放入应用的 wwwroot 文件夹中,这会导致这些文件在下一次构建时被误认为是输入文件。 +- 开发者没有为这些工具正确声明输入和输出依赖,导致即使输出文件已是最新,工具仍会重复运行,从而增加构建耗时。 + +面对这些问题,M$提供了一个Alpha状态的库`Microsoft.AspNetCore.ClientAssets`来解决这个问题。不幸的是,这个库已经因为年久失修(上一次[更新](https://github.com/aspnet/AspLabs/pull/572)是在3年前,引入对于.NET 7的支持),在.NET 9引入新的静态资产部署管线之后,使用会直接报错了。 + +^^ 相关的Issues链接:[#38445](https://github.com/dotnet/aspnetcore/issues/38445),[#62925](https://github.com/dotnet/aspnetcore/issues/62925) + +为了良好地解决如上的问题,我们需要首先了解一下ASP.NET Core中静态资产文件的构建和部署过程。 + +### StaticWebAssetsSdk + +在.NET中,构建静态资产文件的相关代码在[dotnet/sdk](https://github.com/dotnet/sdk)仓库中,称作`StaticWebAssetsSdk`。 + +静态 Web 资源会接管应用程序 wwwroot 文件夹中的内容项,并全面管理这些内容。在开发过程中,系统会生成一个 JSON 清单(manifest),其中包含以下信息: + +- 版本号(version number):标识清单格式的版本。 + +- 清单内容的哈希值(hash):用于判断清单内容是否发生变化。 + +- 库的包 ID(library package id):用于区分当前项目与其他项目所提供的资源。 + +- 库的资源基础路径(asset base path):在将其他库的路径应用“发现模式”时,用于确定要添加的基础路径。 + +- 清单模式(manifest mode):定义来自特定项目的资源在构建和发布时应如何处理。 + +- 相关项目清单及其哈希值的列表:用于判断自清单生成以来,项目引用是否发生变化,或是否有清单被更新。 + +- “发现模式”(discovery patterns)列表:用于在清单构建完成后,有选择性地在运行时提供某些资源。例如,可以使用如下模式: + + ```json + { "Path": "/Pages", "BasePath": "_content/Something", "Pattern": "**/*.js" } + ``` + + 表示仅提供该目录下扩展名为`js`的文件。如果有人添加了图片或其他文件,它们将不会被提供。(这一点很重要,因为这些文件并不符合任何资源规则,也不会包含在发布输出目录中。) + +- 构建/发布过程中生成的静态 Web 资源列表。 + +系统会生成两套清单:**构建清单(build manifest)** 和 **发布清单(publish manifest)**: + +- **构建清单**在构建过程中生成,用于开发阶段,使资源表现得如同它们属于应用程序本身。 +- **发布清单**在发布过程中生成,记录了发布阶段对资源执行的所有转换操作。 + +资源可以在构建阶段或发布阶段定义,并可在任意阶段被标记为“仅构建”或“仅发布”。例如,你可以有两个文件:一个用于开发,一个用于发布,但它们都需要通过相同的 URL 路径提供服务。`service-worker.js` 就是一个典型例子。 + +**构建时清单**由项目中发现的资源以及来自被引用项目和包的资源共同组成。 +**发布清单**则以构建清单为基础,过滤掉仅用于构建的文件,并包含在发布过程中对这些文件执行的所有转换(如链接、打包、压缩等)。 + +这种机制使得在发布阶段可以执行如链接(Linking)、打包(Bundling)、压缩(Compression)等优化操作。被引用的项目也会生成自己的发布清单,其内容会在发布过程中与当前项目的清单合并。同时,在发布过程中,我们仍会保留被引用项目的原始构建清单,以便应用程序可以选择忽略被引用项目的发布资源,并对整个依赖传递闭包中的资源执行全局优化。例如,一个类库在发布时可能生成一个压缩后的 JS 包,而主应用可以选择不使用多个独立的包,而是收集所有原始构建阶段的资源,生成一个统一的包。通常情况下,构建清单和发布清单内容相同,除非存在仅在发布阶段才应用的转换。 + +每份清单中会列出在构建/发布过程中生成或计算出的所有资源及其属性。这些属性包括: + +- **Identity**:资源的唯一标识(文件的完整路径)。 +- **SourceType**:资源类型('Discovered', 'Computed', 'Project', 'Package')。 +- **ContentRoot**:开发阶段资源暴露的原始路径。 +- **BasePath**:资源暴露的基础路径。 +- **RelativePath**:资源的相对路径。 +- **AssetKind**:资源用途('Build', 'Publish', 'All'),由 `CopyToOutputDirectory` / `CopyToPublishDirectory` 推断得出。 +- **AssetMode**:资源作用范围('CurrentProject', 'Reference', 'All')。 +- **AssetRole**:资源角色('Primary', 'Related', 'Alternative')。 +- **AssetMergeSource**:当资源被嵌入到其他 TFM(目标框架)时的来源。 +- **AssetMergeBehavior**:当同一 TFM 中出现资源冲突时的合并行为。 +- **RelatedAsset**:当前资源所依赖的主资源的 Identity。 +- **AssetTraitName**:区分相关或替代资源与主资源的特征名称(如语言、编码格式等)。 +- **AssetTraitValue**:该特征的具体值。 +- **CopyToBuildDirectory**:与 Content 项一致(如 PreserveNewest、Always)。 +- **CopyToPublishDirectory**:与 Content 项一致。 +- **OriginalItemSpec**:定义该资源的原始项规范。 + +关于资源在不同场景下的使用(作为主项目的一部分,或作为被引用项目的一部分),有三种可能的选项: + +- **All**:资源在所有情况下都应被使用。 +- **Root**:资源仅在当前项目作为主项目构建时使用。 +- **Reference**:资源仅在当前项目被其他项目引用时使用。 + +例如,CSS 隔离(CSS isolation)生成的两个包: + +- `<>.styles.css` 是 **Root** 资源,仅在作为主项目时使用。 +- `<>.lib.bundle.css` 是 **Reference** 资源,仅在被其他项目引用时使用。 + +除了上述三种使用模式,项目还需定义其在构建和发布过程中如何处理清单中的文件。对此有三种模式: + +- **Default**:项目在发布时将所有内容复制到发布输出目录,但当被其他项目引用时不做任何操作,而是期望引用方负责处理静态 Web 资源的发布。 + → 通常用于类库(class libraries)。 +- **Root**:项目被视为静态 Web 资源的“根”,即使被引用,其资源也应像主项目一样被处理(例如,不复制传递依赖资源,而只复制 Root 资源)。 + → 用于如 Blazor WebAssembly 托管项目(被 ASP.NET Core 主机项目引用,但资源应视为根项目)。 +- **Isolated**:与 Root 类似,但引用项目完全不知道静态 Web 资源的存在;当前项目会自行在发布时设置处理程序,将资源复制到正确位置。 + → 用于如 Blazor 桌面应用,将静态 Web 资源自动纳入 `GetCopyToPublishDirectoryItems`,使引用方无需了解静态 Web 资源机制。 + +关于资源类型,静态 Web 资源可分为四类: + +- **Discovered assets**:从项目中已有项(如 Content、None 等)中发现的资源。 +- **Computed assets**:在构建过程中生成、需要在构建时复制到最终位置的资源。 +- **Project**:来自被引用项目的资源。当合并被引用项目的清单时,其 Discovered 和 Computed 资源会转换为 Project 类型。 +- **Package**:来自被引用 NuGet 包的资源。 + +关于资源角色(Asset Role),有三种: + +- **Primary(主资源)**:表示可通过其相对路径直接访问的资源。大多数资源属于此类。 +- **Related(相关资源)**:表示与另一个资源相关,但两者都可通过各自的相对路径独立访问。 +- **Alternative(替代资源)**:表示是另一个资源的替代形式,例如预压缩版本或不同格式版本。通常应通过与主资源相同的相对路径提供(具体实现由运行时决定)。静态 Web 资源层仅记录这种关系。 + +对于 Related 和 Alternative 资源,其 `RelatedAsset` 属性指向其所依赖的主资源。这种依赖链可多层嵌套,以表示一个资源的多种表示形式。静态 Web 资源仅记录这些信息,具体如何使用由 MSBuild 目标决定。 + +`AssetTraitName` 和 `AssetTraitValue` 用于区分相关/替代资源与其主资源。例如: + +- 对于全球化程序集,可记录程序集的文化(culture); +- 对于压缩资源,可记录编码方式(如 gzip、brotli)。 + +下图展示了在构建过程中被调用的MSBuild Target: + +![image-20251231225433184](./aspnetcore-swa/image-20251231225433184.webp) + +Sdk提供了一些重要的MSBuild Task供程序员调用: + +- `DefineStaticWebAssets`:该Task扫描提供了一系列候选的资产文件并构建一个*标准化的*静态资产对象; +- `DefineStaticWebAssetEndpoints`:该Task以上一个任务输出的静态资产对象为输入,输出每个静态资产文件的Web终结点; + +在构建中过程中`GenerateStaticWebAssetsManifest`和`GenerateStaticWebAssetsDevelopmentManifest`、`GenerateStaticWebAssetEndpointsManifest`等几个任务会产生一个重要的清单文件,这些文件通常存放在*obj*文件夹中,名称为`staticwebassets.*.json`。其中一个较为重要的清单文件是`staticwebassets.development.json`,其存储了所有的静态资产文件和对应的存储目录。这个文件在构建的过程中会被复制到输出目录`bin`中,名称为`$(PackageId).staticwebassets.runtime.json`。这个文件将会在生产模式下被静态资产中间件读取,作为建立静态文件终结点到实际物理文件的索引。这个文件也为需要调试`StaticWebAssetsSdk`的程序员提供了重要的调试信息,是解决ASP.NET Core中静态资产问题的不二法门。 + +### 解决方案 + +现在已经充分了解了`StaticWebAssetsSdk`,可以来设计在MSBuild中集成前端工具并生成最终静态资产文件的管线了。 + +首先来研究过程的步骤,`npm`或者其类似物也使用类似于MSBuild的先还原再构建两步,首先需要安装程序中使用到的包,然后在运行构建指令构建对应的静态文件,构建完成之后还需要将构建产物交给MSBuild中的静态资产处理管线进行进一步的处理。因此设计如下的三个步骤: + +1. `RestoreClientAssets`,这个Target需要运行`npm install`或者类似的指令安装依赖包; +2. `BuildClientAssets`,这个Target运行`npm run build`或者类似的指令构建项目; +3. `DefineClientAssets`,这个Target调用`DefineStaticWebAssets`等Task声明静态资产文件。 + +确定好生成步骤之后,声明一下会在生成过程中会用到的,可以提供给用户自定义的属性。安装和构建的相应软件包肯定是需要提供给用户自定义的。在一般情况下,前端工具链把将静态文件生成到`dist`文件夹中。为了符合MSBuild的惯例,这里将中间静态文件生成到*obj*文件夹下的`ClientAssets`文件夹中。为了实现这一点,构建过程中的指令就需要支持一个指定生成目录的参数,这个参数也作为一个属性暴露给用户可以自定义。这里就形成了下面三个提供给用户自定义的参数。 + +```xml + + pnpm install + pnpm run build + --output + +``` + +最终就是完成的构建原始代码了。第一个运行的构建目标`RestoreClientAssets`将会在`DispatchToInnerBuilds`任务运行之后运行,这个目标是MSBuild构建管线中一个不论是针对单架构生成还是多架构生成都只会运行一次的目标,这样在项目需要同时编译到.NET 8和.NET 10的情况下,仍然只会运行前端的安装命令一次。`BuildClientAssets`目标紧接着`RestoreClientAssets`目标的运行而运行,并将所有生成的前端文件添加到`_ClientAssetsBuildOutput`项中。最终的`DefineClientAssets`目标在负责解析项目中的所有静态文件的目标`ResolveWebAssetsConfiguration`运行之前运行,调用`DefineStaticWebAssets`和`DefineStaticWebAssetEndpoints`将前面生成的所有前端静态文件添加到MSBuild的静态文件处理管线中。 + +```xml + + <_RestoreClientAssetsBeforeTargets Condition="'$(TargetFramework)' == ''">DispatchToInnerBuilds + + + + + + + + + + <_ClientAssetsOutputFullPath>$([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)ClientAssets')) + + + + + + + <_ClientAssetsBuildOutput Include="$(IntermediateOutputPath)ClientAssets\**"/> + + + + + + + + + + + + + + + + + +``` + +为了测试如下的代码,可以在项目中新建一个`Directory.Build.targets`文件,将上述的内容复制进去进行测试,当然别忘了用``标签包裹这一切。 diff --git a/YaeBlog/source/posts/aspnetcore-swa/image-20251231225433184.webp b/YaeBlog/source/posts/aspnetcore-swa/image-20251231225433184.webp new file mode 100644 index 0000000..003101d --- /dev/null +++ b/YaeBlog/source/posts/aspnetcore-swa/image-20251231225433184.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e50362d4eaebe435faba8a889442930cc069c9218ea8667ec8941b7f2ac9afc7 +size 81062 From 80e48a2043d01e0a43945eec9ee195050a0ccc2b Mon Sep 17 00:00:00 2001 From: jackfiled Date: Wed, 7 Jan 2026 23:21:09 +0800 Subject: [PATCH 31/41] feat: add update time. Make the link in blog blue. --- YaeBlog/Abstraction/IEssayScanService.cs | 6 ++++ YaeBlog/Commands/YaeBlogCommand.cs | 34 +++++++++++++++++-- YaeBlog/Components/Pages/Essays.razor | 19 +++++++---- YaeBlog/Models/BlogEssay.cs | 5 ++- YaeBlog/Models/MarkdownMetadata.cs | 4 ++- .../EssayStylesPostRenderProcessor.cs | 4 +-- YaeBlog/Services/RendererService.cs | 9 +++-- 7 files changed, 64 insertions(+), 17 deletions(-) diff --git a/YaeBlog/Abstraction/IEssayScanService.cs b/YaeBlog/Abstraction/IEssayScanService.cs index d0d2f5e..dee2a7d 100644 --- a/YaeBlog/Abstraction/IEssayScanService.cs +++ b/YaeBlog/Abstraction/IEssayScanService.cs @@ -6,5 +6,11 @@ public interface IEssayScanService { public Task ScanContents(); + /// + /// 将对应的博客文章保存在磁盘上 + /// + /// + /// 指定对应博客文章是否为草稿。因为BlogContent是不可变对象,因此提供该参数以方便publish的实现。 + /// public Task SaveBlogContent(BlogContent content, bool isDraft = true); } diff --git a/YaeBlog/Commands/YaeBlogCommand.cs b/YaeBlog/Commands/YaeBlogCommand.cs index 2f1d04e..b71539e 100644 --- a/YaeBlog/Commands/YaeBlogCommand.cs +++ b/YaeBlog/Commands/YaeBlogCommand.cs @@ -19,6 +19,7 @@ public sealed class YaeBlogCommand AddWatchCommand(_rootCommand); AddListCommand(_rootCommand); AddNewCommand(_rootCommand); + AddUpdateCommand(_rootCommand); AddPublishCommand(_rootCommand); AddScanCommand(_rootCommand); AddCompressCommand(_rootCommand); @@ -46,7 +47,7 @@ public sealed class YaeBlogCommand WebApplication application = builder.Build(); - application.UseStaticFiles(); + application.MapStaticAssets(); application.UseAntiforgery(); application.UseYaeBlog(); @@ -76,7 +77,7 @@ public sealed class YaeBlogCommand WebApplication application = builder.Build(); - application.UseStaticFiles(); + application.MapStaticAssets(); application.UseAntiforgery(); application.UseYaeBlog(); @@ -109,7 +110,7 @@ public sealed class YaeBlogCommand await essayScanService.SaveBlogContent(new BlogContent( new FileInfo(Path.Combine(blogOption.Value.Root, "drafts", file + ".md")), - new MarkdownMetadata { Title = file, Date = DateTime.Now }, + new MarkdownMetadata { Title = file, Date = DateTimeOffset.Now, UpdateTime = DateTimeOffset.Now }, string.Empty, true, [], [])); Console.WriteLine($"Created new blog '{file}."); @@ -117,6 +118,33 @@ public sealed class YaeBlogCommand new EssayScanServiceBinder()); } + private static void AddUpdateCommand(RootCommand rootCommand) + { + Command newCommand = new("update", "Update the blog essay."); + rootCommand.AddCommand(newCommand); + + Argument filenameArgument = new(name: "blog name", description: "The blog filename to update."); + newCommand.AddArgument(filenameArgument); + + newCommand.SetHandler(async (file, _, _, essayScanService) => + { + Console.WriteLine("HINT: The update command only consider published blogs."); + BlogContents contents = await essayScanService.ScanContents(); + + BlogContent? content = contents.Posts.FirstOrDefault(c => c.BlogName == file); + if (content is null) + { + Console.WriteLine($"Target essay {file} is not exist."); + return; + } + + content.Metadata.UpdateTime = DateTimeOffset.Now; + await essayScanService.SaveBlogContent(content, content.IsDraft); + + }, filenameArgument, + new BlogOptionsBinder(), new LoggerBinder(), new EssayScanServiceBinder()); + } + private static void AddListCommand(RootCommand rootCommand) { Command command = new("list", "List all blogs"); diff --git a/YaeBlog/Components/Pages/Essays.razor b/YaeBlog/Components/Pages/Essays.razor index d1d1b74..37fa6fe 100644 --- a/YaeBlog/Components/Pages/Essays.razor +++ b/YaeBlog/Components/Pages/Essays.razor @@ -19,10 +19,6 @@
    -
    - @(_essay!.PublishTime.ToString("yyyy-MM-dd")) -
    - @foreach (string tag in _essay!.Tags) {
    @@ -32,10 +28,19 @@
    }
    -
    -
    -
    +
    + 发布于: @(_essay.PublishTime.ToString("yyyy年MM月dd日 hh:mm:ss")) +
    + + @if (_essay.UpdateTime != _essay.PublishTime) + { +
    + 更新于: @(_essay.UpdateTime.ToString("yyyy年MM月dd日 hh:mm:ss")) +
    + } + +
    总字数:@(_essay!.WordCount)字,预计阅读时间 @(_essay!.ReadTime)。
    diff --git a/YaeBlog/Models/BlogEssay.cs b/YaeBlog/Models/BlogEssay.cs index 0af5cf6..5b71515 100644 --- a/YaeBlog/Models/BlogEssay.cs +++ b/YaeBlog/Models/BlogEssay.cs @@ -8,7 +8,9 @@ public class BlogEssay : IComparable public required bool IsDraft { get; init; } - public required DateTime PublishTime { get; init; } + public required DateTimeOffset PublishTime { get; init; } + + public required DateTimeOffset UpdateTime { get; init; } public required string Description { get; init; } @@ -28,6 +30,7 @@ public class BlogEssay : IComparable FileName = FileName, IsDraft = IsDraft, PublishTime = PublishTime, + UpdateTime = UpdateTime, Description = Description, WordCount = WordCount, ReadTime = ReadTime, diff --git a/YaeBlog/Models/MarkdownMetadata.cs b/YaeBlog/Models/MarkdownMetadata.cs index 8359e7d..7ef93fa 100644 --- a/YaeBlog/Models/MarkdownMetadata.cs +++ b/YaeBlog/Models/MarkdownMetadata.cs @@ -4,7 +4,9 @@ public class MarkdownMetadata { public string? Title { get; set; } - public DateTime? Date { get; set; } + public DateTimeOffset Date { get; set; } + + public DateTimeOffset UpdateTime { get; set; } public List? Tags { get; set; } } diff --git a/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs b/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs index a9d2321..3d05838 100644 --- a/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs +++ b/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs @@ -16,8 +16,7 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor public async Task ProcessAsync(BlogEssay essay) { BrowsingContext context = new(Configuration.Default); - IDocument document = await context.OpenAsync( - req => req.Content(essay.HtmlContent)); + IDocument document = await context.OpenAsync(req => req.Content(essay.HtmlContent)); ApplyGlobalCssStyles(document); BeatifyTable(document); @@ -36,6 +35,7 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor { "h5", "text-lg font-bold py-1" }, { "p", "p-2" }, { "img", "w-11/12 block mx-auto my-2 rounded-md shadow-md" }, + { "a", "text-blue-600" } }; private void ApplyGlobalCssStyles(IDocument document) diff --git a/YaeBlog/Services/RendererService.cs b/YaeBlog/Services/RendererService.cs index 6105694..8317314 100644 --- a/YaeBlog/Services/RendererService.cs +++ b/YaeBlog/Services/RendererService.cs @@ -47,7 +47,10 @@ public partial class RendererService( Description = GetDescription(content), WordCount = wordCount, ReadTime = CalculateReadTime(wordCount), - PublishTime = content.Metadata.Date ?? DateTime.Now, + PublishTime = content.Metadata.Date == default ? DateTimeOffset.Now : content.Metadata.Date, + // 如果不存在最后的更新时间,就把更新时间设置为发布时间 + UpdateTime = + content.Metadata.UpdateTime == default ? content.Metadata.Date : content.Metadata.UpdateTime, HtmlContent = content.Content }; @@ -182,7 +185,7 @@ public partial class RendererService( string description = builder.ToString(); - logger.LogDebug("Description of {} is {}.", content.BlogName, + logger.LogDebug("Description of {name} is {desc}.", content.BlogName, description); return description; } @@ -193,7 +196,7 @@ public partial class RendererService( where char.IsLetterOrDigit(c) select c).Count(); - logger.LogDebug("Word count of {} is {}", content.BlogName, + logger.LogDebug("Word count of {blog} is {count}", content.BlogName, count); return (uint)count; } From 35f069f40a420540ff8da6d9254cc12dc5367a90 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sat, 10 Jan 2026 19:46:06 +0800 Subject: [PATCH 32/41] feat: move TOBs to the left of essays. Fix the word counter to not count the characters in code blocks. Signed-off-by: jackfiled --- .gitea/workflows/build.yaml | 4 +- YaeBlog.Tests/DateTimeOffsetTests.cs | 13 +++++ YaeBlog.Tests/YaeBlog.Tests.csproj | 25 +++++++++ YaeBlog.slnx | 1 + YaeBlog/Components/Pages/Essays.razor | 69 ++++++++++++------------- YaeBlog/Dockerfile | 2 +- YaeBlog/Services/MarkdownWordCounter.cs | 62 ++++++++++++++++++++++ YaeBlog/Services/RendererService.cs | 21 +++----- YaeBlog/build.ps1 | 28 +++++++++- 9 files changed, 171 insertions(+), 54 deletions(-) create mode 100644 YaeBlog.Tests/DateTimeOffsetTests.cs create mode 100644 YaeBlog.Tests/YaeBlog.Tests.csproj create mode 100644 YaeBlog/Services/MarkdownWordCounter.cs diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index fe5c0ab..84b530f 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -13,9 +13,7 @@ jobs: lfs: true - name: Build project. run: | - proxy - podman pull mcr.microsoft.com/dotnet/aspnet:10.0 - unproxy + podman pull mcr.azure.cn/dotnet/aspnet:10.0 cd YaeBlog pwsh build.ps1 build - name: Workaround to make sure podman-login working. diff --git a/YaeBlog.Tests/DateTimeOffsetTests.cs b/YaeBlog.Tests/DateTimeOffsetTests.cs new file mode 100644 index 0000000..f9fd8db --- /dev/null +++ b/YaeBlog.Tests/DateTimeOffsetTests.cs @@ -0,0 +1,13 @@ +namespace YaeBlog.Tests; + +public class DateTimeOffsetTests +{ + [Fact] + public void DateTimeOffsetParseTest() + { + const string input = "2026-01-04T16:36:36.5629759+08:00"; + DateTimeOffset time = DateTimeOffset.Parse(input); + + Assert.Equal("2026年01月04日 16:36:36", time.ToString("yyyy年MM月dd日 HH:mm:ss")); + } +} diff --git a/YaeBlog.Tests/YaeBlog.Tests.csproj b/YaeBlog.Tests/YaeBlog.Tests.csproj new file mode 100644 index 0000000..5d5e7c6 --- /dev/null +++ b/YaeBlog.Tests/YaeBlog.Tests.csproj @@ -0,0 +1,25 @@ + + + + net10.0 + enable + enable + false + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/YaeBlog.slnx b/YaeBlog.slnx index 53e014e..4cf5583 100644 --- a/YaeBlog.slnx +++ b/YaeBlog.slnx @@ -10,5 +10,6 @@ + diff --git a/YaeBlog/Components/Pages/Essays.razor b/YaeBlog/Components/Pages/Essays.razor index 37fa6fe..9aa0a94 100644 --- a/YaeBlog/Components/Pages/Essays.razor +++ b/YaeBlog/Components/Pages/Essays.razor @@ -12,52 +12,42 @@
    -

    @(_essay!.Title)

    -
    -
    -
    +
    +
    +

    @(_essay!.Title)

    +
    -
    -
    - @foreach (string tag in _essay!.Tags) +
    + @foreach (string tag in _essay!.Tags) + { + + } +
    + +
    + 发布于: @(_essay.PublishTime.ToString("yyyy年MM月dd日 HH:mm:ss")) +
    + + @if (_essay.UpdateTime != _essay.PublishTime) { -
    - - # @(tag) - +
    + 更新于: @(_essay.UpdateTime.ToString("yyyy年MM月dd日 HH:mm:ss"))
    } -
    -
    - 发布于: @(_essay.PublishTime.ToString("yyyy年MM月dd日 hh:mm:ss")) -
    - - @if (_essay.UpdateTime != _essay.PublishTime) - {
    - 更新于: @(_essay.UpdateTime.ToString("yyyy年MM月dd日 hh:mm:ss")) + 总字数:@(_essay!.WordCount)字,预计阅读时间 @(_essay!.ReadTime)
    - } - -
    - 总字数:@(_essay!.WordCount)字,预计阅读时间 @(_essay!.ReadTime)。
    -
    -
    - @((MarkupString)_essay!.HtmlContent) -
    - -
    - -
    -
    -
    -
    +

    文章目录

    @@ -98,8 +88,17 @@ }
    -
    +
    +
    + @((MarkupString)_essay!.HtmlContent) +
    + +
    + +
    +
    +
    @code { diff --git a/YaeBlog/Dockerfile b/YaeBlog/Dockerfile index 933638a..021e1d0 100644 --- a/YaeBlog/Dockerfile +++ b/YaeBlog/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/aspnet:10.0 +FROM mcr.azure.cn/dotnet/aspnet:10.0 ARG COMMIT_ID ENV COMMIT_ID=${COMMIT_ID} diff --git a/YaeBlog/Services/MarkdownWordCounter.cs b/YaeBlog/Services/MarkdownWordCounter.cs new file mode 100644 index 0000000..b5d3a84 --- /dev/null +++ b/YaeBlog/Services/MarkdownWordCounter.cs @@ -0,0 +1,62 @@ +using YaeBlog.Extensions; +using YaeBlog.Models; + +namespace YaeBlog.Services +{ + public class MarkdownWordCounter + { + private bool _inCodeBlock; + private int _index; + private readonly string _content; + + private uint WordCount { get; set; } + + private MarkdownWordCounter(BlogContent content) + { + _content = content.Content; + } + + private void CountWordInner() + { + while (_index < _content.Length) + { + if (IsCodeBlockTag()) + { + _inCodeBlock = !_inCodeBlock; + } + + if (!_inCodeBlock && char.IsLetterOrDigit(_content, _index)) + { + WordCount += 1; + } + + _index++; + } + } + + private bool IsCodeBlockTag() + { + // 首先考虑识别代码块 + bool outerCodeBlock = + Enumerable.Range(0, 3) + .Select(i => _index + i < _content.Length && _content.AsSpan().Slice(_index + i, 1) is "`") + .All(i => i); + + if (outerCodeBlock) + { + return true; + } + + // 然后识别行内代码 + return _index < _content.Length && _content.AsSpan().Slice(_index, 1) is "`"; + } + + public static uint CountWord(BlogContent content) + { + MarkdownWordCounter counter = new(content); + counter.CountWordInner(); + + return counter.WordCount; + } + } +} diff --git a/YaeBlog/Services/RendererService.cs b/YaeBlog/Services/RendererService.cs index 8317314..7069a10 100644 --- a/YaeBlog/Services/RendererService.cs +++ b/YaeBlog/Services/RendererService.cs @@ -38,7 +38,7 @@ public partial class RendererService( List essays = []; foreach (BlogContent content in preProcessedContents) { - uint wordCount = GetWordCount(content); + (uint wordCount, string readTime) = GetWordCount(content); BlogEssay essay = new() { Title = content.Metadata.Title ?? content.BlogName, @@ -46,7 +46,7 @@ public partial class RendererService( IsDraft = content.IsDraft, Description = GetDescription(content), WordCount = wordCount, - ReadTime = CalculateReadTime(wordCount), + ReadTime = readTime, PublishTime = content.Metadata.Date == default ? DateTimeOffset.Now : content.Metadata.Date, // 如果不存在最后的更新时间,就把更新时间设置为发布时间 UpdateTime = @@ -190,23 +190,16 @@ public partial class RendererService( return description; } - private uint GetWordCount(BlogContent content) + private (uint, string) GetWordCount(BlogContent content) { - int count = (from c in content.Content - where char.IsLetterOrDigit(c) - select c).Count(); + uint count = MarkdownWordCounter.CountWord(content); logger.LogDebug("Word count of {blog} is {count}", content.BlogName, count); - return (uint)count; - } - - private static string CalculateReadTime(uint wordCount) - { // 据说语文教学大纲规定,中国高中生阅读现代文的速度是600字每分钟 - int second = (int)wordCount / 10; - TimeSpan span = new(0, 0, second); + uint second = count / 10; + TimeSpan span = new(0, 0, (int)second); - return span.ToString("mm'分 'ss'秒'"); + return (count, span.ToString("mm'分'ss'秒'")); } } diff --git a/YaeBlog/build.ps1 b/YaeBlog/build.ps1 index 0242036..088c7f0 100755 --- a/YaeBlog/build.ps1 +++ b/YaeBlog/build.ps1 @@ -3,7 +3,7 @@ [cmdletbinding()] param( [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Specify the build target")] - [ValidateSet("tailwind", "watch", "publish", "compress", "build")] + [ValidateSet("tailwind", "watch", "publish", "compress", "build", "dev")] [string]$Target, [string]$Output = "wwwroot", [string]$Essay, @@ -56,6 +56,28 @@ process { podman build . -t ccr.ccs.tencentyun.com/jackfiled/blog --build-arg COMMIT_ID=$commitId } + function Start-Develop { + Write-Host "Start tailwindcss and dotnet watch servers..." + $pnpmProcess = Start-Process pnpm "tailwindcss -i wwwroot/tailwind.css -o obj/Debug/net10.0/ClientAssets/tailwind.g.css -w" ` + -PassThru + + try + { + Write-Host "Started pnpm process exit? " $pnpmProcess.HasExited + Start-Process dotnet "watch -- serve" -PassThru | Wait-Process + } + finally + { + if ($pnpmProcess.HasExited) + { + Write-Error "pnpm process has exited!" + exit 1 + } + Write-Host "Kill tailwindcss and dotnet watch servers..." + $pnpmProcess | Stop-Process + } + } + switch ($Target) { "tailwind" { @@ -85,6 +107,10 @@ process { Build-Image break } + "dev" { + Start-Develop + break + } } } From dd81e9a6f4399b99f5309902c4f20edaedf8aad7 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Wed, 14 Jan 2026 23:19:29 +0800 Subject: [PATCH 33/41] fix: Use string for date field of markdown metadata POCO. Add new command for build.ps1 script. Signed-off-by: jackfiled --- YaeBlog/Commands/YaeBlogCommand.cs | 13 +++++++++---- YaeBlog/Components/Pages/Archives.razor | 8 ++++---- YaeBlog/Components/Pages/Index.razor | 2 +- YaeBlog/Models/MarkdownMetadata.cs | 4 ++-- YaeBlog/Services/RendererService.cs | 14 ++++++++++---- YaeBlog/build.ps1 | 18 +++++++++++++----- 6 files changed, 39 insertions(+), 20 deletions(-) diff --git a/YaeBlog/Commands/YaeBlogCommand.cs b/YaeBlog/Commands/YaeBlogCommand.cs index b71539e..770b339 100644 --- a/YaeBlog/Commands/YaeBlogCommand.cs +++ b/YaeBlog/Commands/YaeBlogCommand.cs @@ -110,7 +110,12 @@ public sealed class YaeBlogCommand await essayScanService.SaveBlogContent(new BlogContent( new FileInfo(Path.Combine(blogOption.Value.Root, "drafts", file + ".md")), - new MarkdownMetadata { Title = file, Date = DateTimeOffset.Now, UpdateTime = DateTimeOffset.Now }, + new MarkdownMetadata + { + Title = file, + Date = DateTimeOffset.Now.ToString("o"), + UpdateTime = DateTimeOffset.Now.ToString("o") + }, string.Empty, true, [], [])); Console.WriteLine($"Created new blog '{file}."); @@ -138,9 +143,8 @@ public sealed class YaeBlogCommand return; } - content.Metadata.UpdateTime = DateTimeOffset.Now; + content.Metadata.UpdateTime = DateTimeOffset.Now.ToString("o"); await essayScanService.SaveBlogContent(content, content.IsDraft); - }, filenameArgument, new BlogOptionsBinder(), new LoggerBinder(), new EssayScanServiceBinder()); } @@ -239,7 +243,8 @@ public sealed class YaeBlogCommand } // 设置发布的时间 - content.Metadata.Date = DateTime.Now; + content.Metadata.Date = DateTimeOffset.Now.ToString("o"); + content.Metadata.UpdateTime = DateTimeOffset.Now.ToString("o"); // 将选中的博客文件复制到posts await essayScanService.SaveBlogContent(content, isDraft: false); diff --git a/YaeBlog/Components/Pages/Archives.razor b/YaeBlog/Components/Pages/Archives.razor index 883f33b..55204f0 100644 --- a/YaeBlog/Components/Pages/Archives.razor +++ b/YaeBlog/Components/Pages/Archives.razor @@ -19,7 +19,7 @@
    - @foreach (IGrouping group in _essays) + @foreach (IGrouping group in _essays) {
    @@ -30,7 +30,7 @@
    @foreach (BlogEssay essay in group) { - +
    @(essay.PublishTime.ToString("MM月dd日")) @@ -51,13 +51,13 @@
    @code { - private readonly List> _essays = []; + private readonly List> _essays = []; protected override void OnInitialized() { base.OnInitialized(); _essays.AddRange(from essay in Contents.Essays - group essay by new DateTime(essay.PublishTime.Year, 1, 1)); + group essay by new DateTimeOffset(essay.PublishTime.Year, 1, 1,0, 0, 0, TimeSpan.Zero)); } } diff --git a/YaeBlog/Components/Pages/Index.razor b/YaeBlog/Components/Pages/Index.razor index dab799c..96c96b1 100644 --- a/YaeBlog/Components/Pages/Index.razor +++ b/YaeBlog/Components/Pages/Index.razor @@ -26,7 +26,7 @@

    - 平平无奇的计算机科学与技术学徒,连微小的贡献都没做。 + 学过一些基础的计算机知识,略懂一些代码。

    diff --git a/YaeBlog/Models/MarkdownMetadata.cs b/YaeBlog/Models/MarkdownMetadata.cs index 7ef93fa..9aab220 100644 --- a/YaeBlog/Models/MarkdownMetadata.cs +++ b/YaeBlog/Models/MarkdownMetadata.cs @@ -4,9 +4,9 @@ public class MarkdownMetadata { public string? Title { get; set; } - public DateTimeOffset Date { get; set; } + public string? Date { get; set; } - public DateTimeOffset UpdateTime { get; set; } + public string? UpdateTime { get; set; } public List? Tags { get; set; } } diff --git a/YaeBlog/Services/RendererService.cs b/YaeBlog/Services/RendererService.cs index 7069a10..69b648c 100644 --- a/YaeBlog/Services/RendererService.cs +++ b/YaeBlog/Services/RendererService.cs @@ -39,6 +39,14 @@ public partial class RendererService( foreach (BlogContent content in preProcessedContents) { (uint wordCount, string readTime) = GetWordCount(content); + DateTimeOffset publishDate = content.Metadata.Date is null + ? DateTimeOffset.Now + : DateTimeOffset.Parse(content.Metadata.Date); + // 如果不存在最后的更新时间,就把更新时间设置为发布时间 + DateTimeOffset updateTime = content.Metadata.UpdateTime is null + ? publishDate + : DateTimeOffset.Parse(content.Metadata.UpdateTime); + BlogEssay essay = new() { Title = content.Metadata.Title ?? content.BlogName, @@ -47,10 +55,8 @@ public partial class RendererService( Description = GetDescription(content), WordCount = wordCount, ReadTime = readTime, - PublishTime = content.Metadata.Date == default ? DateTimeOffset.Now : content.Metadata.Date, - // 如果不存在最后的更新时间,就把更新时间设置为发布时间 - UpdateTime = - content.Metadata.UpdateTime == default ? content.Metadata.Date : content.Metadata.UpdateTime, + PublishTime = publishDate, + UpdateTime = updateTime, HtmlContent = content.Content }; diff --git a/YaeBlog/build.ps1 b/YaeBlog/build.ps1 index 088c7f0..7e4b350 100755 --- a/YaeBlog/build.ps1 +++ b/YaeBlog/build.ps1 @@ -3,7 +3,7 @@ [cmdletbinding()] param( [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Specify the build target")] - [ValidateSet("tailwind", "watch", "publish", "compress", "build", "dev")] + [ValidateSet("tailwind", "publish", "compress", "build", "dev", "new")] [string]$Target, [string]$Output = "wwwroot", [string]$Essay, @@ -21,6 +21,15 @@ begin { exit 1 } } + + if ($Target -eq "new") + { + if ($Essay -eq "") + { + Write-Error "No new name, please add with --essay argument." + exit 1 + } + } } process { @@ -85,10 +94,6 @@ process { pnpm tailwindcss -i wwwroot/tailwind.css -o $Output/tailwind.g.css break } - "watch" { - dotnet run -- watch - break - } "publish" { Write-Host "Publish essay $Essay..." dotnet run -- publish $Essay @@ -111,6 +116,9 @@ process { Start-Develop break } + "new" { + dotnet run -- new $Essay + } } } From fa01b74f09ef687aaf00408d9450726723d6a6de Mon Sep 17 00:00:00 2001 From: jackfiled Date: Sun, 18 Jan 2026 20:15:08 +0800 Subject: [PATCH 34/41] fix: the unordered list has no style. Signed-off-by: jackfiled --- .../EssayStylesPostRenderProcessor.cs | 28 +++++++++++++++---- YaeBlog/Services/RendererService.cs | 2 +- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs b/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs index 3d05838..77a7b4f 100644 --- a/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs +++ b/YaeBlog/Processors/EssayStylesPostRenderProcessor.cs @@ -102,17 +102,33 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor } } + /// + /// 美化各种列表元素 + /// + /// private static void BeatifyList(IDocument document) { - foreach (IElement ulElement in from e in document.All - where e.LocalName == "ul" + foreach (IElement listElement in from e in document.All + where e.LocalName is "ol" or "ul" select e) { - // 首先给