Compare commits
18 Commits
a254d0123d
...
feat/intro
| Author | SHA1 | Date | |
|---|---|---|---|
|
a3791596da
|
|||
|
2be09b8319
|
|||
|
fa01b74f09
|
|||
|
dd81e9a6f4
|
|||
|
35f069f40a
|
|||
|
80e48a2043
|
|||
|
1be39327aa
|
|||
|
c050d1b790
|
|||
|
56374a4e6b
|
|||
|
58ba4b2a2f
|
|||
|
009e86b553
|
|||
|
d1ec3a51d1
|
|||
|
dab866f13a
|
|||
| 94421168c6 | |||
| 938fe1c715 | |||
| eedfc1ffce | |||
| 0f346d9ded | |||
| a662ecc14b |
@@ -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
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -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
|
||||
|
||||
@@ -1,33 +1,30 @@
|
||||
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
|
||||
- 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_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: |
|
||||
podman pull mcr.azure.cn/dotnet/aspnet:10.0
|
||||
cd YaeBlog
|
||||
pwsh build.ps1 build
|
||||
- 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
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "third-party/BlazorSvgComponents"]
|
||||
path = third-party/BlazorSvgComponents
|
||||
url = https://git.rrricardo.top/jackfiled/BlazorSvgComponents.git
|
||||
13
YaeBlog.Tests/DateTimeOffsetTests.cs
Normal file
13
YaeBlog.Tests/DateTimeOffsetTests.cs
Normal file
@@ -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"));
|
||||
}
|
||||
}
|
||||
25
YaeBlog.Tests/YaeBlog.Tests.csproj
Normal file
25
YaeBlog.Tests/YaeBlog.Tests.csproj
Normal file
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\YaeBlog\YaeBlog.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -10,5 +10,6 @@
|
||||
<File Path="LICENSE" />
|
||||
<File Path="README.md" />
|
||||
</Folder>
|
||||
<Project Path="YaeBlog.Tests/YaeBlog.Tests.csproj" />
|
||||
<Project Path="YaeBlog/YaeBlog.csproj" />
|
||||
</Solution>
|
||||
|
||||
@@ -6,5 +6,11 @@ public interface IEssayScanService
|
||||
{
|
||||
public Task<BlogContents> ScanContents();
|
||||
|
||||
/// <summary>
|
||||
/// 将对应的博客文章保存在磁盘上
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="isDraft">指定对应博客文章是否为草稿。因为BlogContent是不可变对象,因此提供该参数以方便publish的实现。</param>
|
||||
/// <returns></returns>
|
||||
public Task SaveBlogContent(BlogContent content, bool isDraft = true);
|
||||
}
|
||||
|
||||
@@ -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,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 = DateTime.Now },
|
||||
new MarkdownMetadata
|
||||
{
|
||||
Title = file,
|
||||
Date = DateTimeOffset.Now.ToString("o"),
|
||||
UpdateTime = DateTimeOffset.Now.ToString("o")
|
||||
},
|
||||
string.Empty, true, [], []));
|
||||
|
||||
Console.WriteLine($"Created new blog '{file}.");
|
||||
@@ -117,6 +123,32 @@ public sealed class YaeBlogCommand
|
||||
new EssayScanServiceBinder());
|
||||
}
|
||||
|
||||
private static void AddUpdateCommand(RootCommand rootCommand)
|
||||
{
|
||||
Command newCommand = new("update", "Update the blog essay.");
|
||||
rootCommand.AddCommand(newCommand);
|
||||
|
||||
Argument<string> 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.ToString("o");
|
||||
await essayScanService.SaveBlogContent(content, content.IsDraft);
|
||||
}, filenameArgument,
|
||||
new BlogOptionsBinder(), new LoggerBinder<EssayScanService>(), new EssayScanServiceBinder());
|
||||
}
|
||||
|
||||
private static void AddListCommand(RootCommand rootCommand)
|
||||
{
|
||||
Command command = new("list", "List all blogs");
|
||||
@@ -211,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);
|
||||
|
||||
@@ -3,18 +3,44 @@
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta lang="zh-CN"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<base href="/"/>
|
||||
<link rel="stylesheet" href="YaeBlog.styles.css"/>
|
||||
<link rel="icon" href="images/favicon.ico"/>
|
||||
<link rel="stylesheet" href="globals.css"/>
|
||||
<link rel="stylesheet" href="tailwind.g.css"/>
|
||||
<link rel="stylesheet" href="@Assets["YaeBlog.styles.css"]"/>
|
||||
<link rel="icon" href="@Assets["images/favicon.ico"]"/>
|
||||
<link rel="stylesheet" href="@Assets["tailwind.g.css"]"/>
|
||||
<style>
|
||||
@@font-face {
|
||||
font-family: "Font Awesome 7 Free";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url(@Assets["fonts/fa-regular-400.woff2"]) format("woff2");
|
||||
}
|
||||
|
||||
@@font-face {
|
||||
font-family: "Font Awesome 7 Free";
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url(@Assets["fonts/fa-solid-900.woff2"]) format("woff2")
|
||||
}
|
||||
|
||||
@@font-face {
|
||||
font-family: "Font Awesome 7 Brands";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url(@Assets["fonts/fa-brands-400.woff2"]) format("woff2")
|
||||
}
|
||||
</style>
|
||||
<HeadOutlet/>
|
||||
<ImportMap/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes/>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<Routes/>
|
||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
29
YaeBlog/Components/AppreciationCode.razor
Normal file
29
YaeBlog/Components/AppreciationCode.razor
Normal file
@@ -0,0 +1,29 @@
|
||||
<div class="flex flex-wrap justify-center gap-12 max-w-md md:max-w-lg">
|
||||
<div class="relative w-40 h-48 md:w-48 md:w-48 overflow-hidden
|
||||
transition-all duration-300 ease-out hover:scale-125 group">
|
||||
<img
|
||||
src="./images/wechat-code.jpeg"
|
||||
alt="微信赞赏码"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
<div class="absolute -bottom-8 left-0 right-0 text-center
|
||||
text-white bg-black opacity-60 text-sm font-medium
|
||||
backdrop-blur-sm group-hover:bottom-2 transition-all duration-300">
|
||||
请我喝奶茶<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative w-40 h-48 md:w-48 md:h-48 overflow-hidden
|
||||
transition-all duration-300 ease-out hover:scale-125 group">
|
||||
<img
|
||||
src="./images/alipay-code.jpeg"
|
||||
alt="支付宝赞赏码"
|
||||
class="w-full h-full object-cover"/>
|
||||
<div class="absolute -bottom-8 left-0 right-0 text-center
|
||||
text-white bg-black opacity-60 text-sm font-medium
|
||||
backdrop-blur-sm group-hover:bottom-2 transition-all duration-300">
|
||||
请我吃晚饭<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<div class="flex flex-col p-3">
|
||||
<div class="text-3xl font-bold py-2">
|
||||
<a href="/blog/essays/@(Essay.FileName)" target="_blank">@(Essay.Title)</a>
|
||||
<a href="@(Essay.EssayLink)">@(Essay.Title)</a>
|
||||
</div>
|
||||
|
||||
<div class="p-2 flex flex-row justify-content-start gap-2">
|
||||
@@ -14,9 +14,7 @@
|
||||
@foreach (string key in Essay.Tags)
|
||||
{
|
||||
<div class="text-sky-600">
|
||||
<a href="/blog/tags/?tagName=@(UrlEncoder.Default.Encode(key))">
|
||||
# @key
|
||||
</a>
|
||||
<Anchor Address="@($"/blog/tags/?tagName={UrlEncoder.Default.Encode(key)}")" Text="@($"# {key}")"/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -2,21 +2,29 @@
|
||||
<div>
|
||||
<p class="text-md">
|
||||
2021 - @(DateTimeOffset.Now.Year) ©
|
||||
<Anchor Address="https://rrricardot.top" Text="Ricardo Ren"/>
|
||||
<Anchor Address="https://rrricardot.top" Text="初冬的朝阳"/>
|
||||
,由
|
||||
<Anchor Address="https://dotnet.microsoft.com" Text="@DotnetVersion"/>
|
||||
驱动。
|
||||
</p>
|
||||
<p class="text-md">
|
||||
Build Commit #
|
||||
<Anchor Address="@BuildCommitUrl" Text="@BuildCommitId"/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-md">
|
||||
<a href="https://beian.miit.gov.cn" target="_blank" class="text-black">蜀ICP备2022004429号-1</a>
|
||||
<Anchor Address="https://beian.miit.gov.cn" Text="蜀ICP备2022004429号-1" NewPage="true"/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
private string DotnetVersion => $".NET {Environment.Version}";
|
||||
private static string DotnetVersion => $".NET {Environment.Version}";
|
||||
|
||||
private static string BuildCommitId => Environment.GetEnvironmentVariable("COMMIT_ID") ?? "local_build";
|
||||
|
||||
private static string BuildCommitUrl => $"https://git.rrricardo.top/jackfiled/YaeBlog/commit/{BuildCommitId}";
|
||||
}
|
||||
|
||||
28
YaeBlog/Components/Layout/BlogLayout.razor
Normal file
28
YaeBlog/Components/Layout/BlogLayout.razor
Normal file
@@ -0,0 +1,28 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
@attribute [StreamRendering]
|
||||
|
||||
<main class="container mx-auto flex flex-col min-h-screen">
|
||||
<div class="grid grid-cols-3 mx-3">
|
||||
<div class="md:col-span-2 col-span-3 h-20 flex items-center">
|
||||
<a href="/blog/">
|
||||
<span class="text-blue-600 text-2xl">Ricardo's Blog</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-1 col-span-3 h-20 flex items-center">
|
||||
<div class="flex flex-row w-full px-2 md:justify-center justify-end text-xl gap-3">
|
||||
<Anchor Address="/blog/archives" Text="归档"/>
|
||||
<Anchor Address="/blog/tags/" Text="标签"/>
|
||||
<Anchor Address="/about/" Text="关于" NewPage="@(true)"/>
|
||||
<Anchor Address="/friends" Text="友链" NewPage="@(true)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-4 py-2 flex-grow">
|
||||
@Body
|
||||
</div>
|
||||
|
||||
<Foonter/>
|
||||
</main>
|
||||
@@ -17,13 +17,11 @@
|
||||
|
||||
<Anchor
|
||||
Address="/about/"
|
||||
Text="关于"
|
||||
NewPage="@(true)"/>
|
||||
Text="关于"/>
|
||||
|
||||
<Anchor
|
||||
Address="/friends"
|
||||
Text="友链"
|
||||
NewPage="@(true)"/>
|
||||
Text="友链"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,6 +1,3 @@
|
||||
@using YaeBlog.Models
|
||||
@inject BlogOptions Options
|
||||
|
||||
<div class="px-4 py-8 border border-sky-700 rounded-md bg-sky-200">
|
||||
<div class="flex flex-col gap-3 text-md">
|
||||
<div>
|
||||
@@ -24,6 +21,17 @@
|
||||
Ricardo's Blog
|
||||
</a>”。
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-center">
|
||||
<p>如果觉得不错的话,可以支持一下作者哦~</p>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<AppreciationCode/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
92
YaeBlog/Components/Pages/About.razor
Normal file
92
YaeBlog/Components/Pages/About.razor
Normal file
@@ -0,0 +1,92 @@
|
||||
@page "/about"
|
||||
|
||||
<PageTitle>
|
||||
关于
|
||||
</PageTitle>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div>
|
||||
<h1 class="text-4xl">关于</h1>
|
||||
</div>
|
||||
|
||||
<div class="py-4">
|
||||
<span class="italic">把字刻在石头上!(・’ω’・)</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col p-2">
|
||||
<div class="flex flex-col p-2">
|
||||
<div class="pb-2">
|
||||
<h3 class="text-2xl">关于我</h3>
|
||||
</div>
|
||||
|
||||
<div class="mx-4">
|
||||
<div class="my-4">
|
||||
<p class="my-2">
|
||||
正在明光村幼儿园附属研究生院攻读计算机科学与技术的硕士学位,研究AI编译器和异构编译器。
|
||||
</p>
|
||||
|
||||
<p class="my-2">
|
||||
一般在互联网上使用<span class="italic">初冬的朝阳</span>或者<span
|
||||
class="italic">jackfiled</span>的名字活动。
|
||||
<span class="line-through">(都是ICP备案过的人了,网名似乎没有太大的用处)</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<p class="my-1">
|
||||
主要是一个C#程序员,目前也在尝试写一点Rust。
|
||||
<span class="line-through">
|
||||
总体上对于编程语言的态度是“大家都是我的翅膀.jpg”。
|
||||
</span>
|
||||
</p>
|
||||
<p class="my-1">
|
||||
写过一些前后端分离的项目,对于RISC-V相关的开发项目也颇感兴趣。
|
||||
</p>
|
||||
<p class="my-1">
|
||||
常常因为现实的压力而写一些C/C++,现在就在和MLIR殊死搏斗。
|
||||
</p>
|
||||
<p class="my-1">
|
||||
日常使用Arch Linux。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<p class="my-1">
|
||||
100%社恐。日常生活是宅在电脑前面自言自语。
|
||||
</p>
|
||||
<p class="my-1">
|
||||
兴趣活动是读书和看番,目前在玩戴森球计划和三角洲。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col p-2">
|
||||
<div class="pb-2">
|
||||
<h3 class="text-2xl">关于本站</h3>
|
||||
</div>
|
||||
|
||||
<div class="mx-4">
|
||||
<div class="my-4">
|
||||
<p class="my-2">
|
||||
本站肇始于2021年下半年,在开始的两年中个人网站和博客是分别的两个网站,个人网站是裸HTML写的,博客是用
|
||||
<Anchor Text="Hexo" Address="https://hexo.io" NewPage="@(true)"/>
|
||||
的。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<p class="my-2">
|
||||
2024年,我们决定使用.NET技术完全重构两个网站,合二为一。虽然目前这个版本还是一个半成品,但是我们一定会努力的~(确信。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<p class="my-2">
|
||||
2025年,我们将使用的样式库从Bootstrap迁移到Tailwind CSS,将现代的前端技术同Blazor结合起来。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -19,7 +19,7 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@foreach (IGrouping<DateTime, BlogEssay> group in _essays)
|
||||
@foreach (IGrouping<DateTimeOffset, BlogEssay> group in _essays)
|
||||
{
|
||||
<div class="p-2">
|
||||
<div class="flex flex-col">
|
||||
@@ -30,7 +30,7 @@
|
||||
<div class="px-4 py-4 flex flex-col">
|
||||
@foreach (BlogEssay essay in group)
|
||||
{
|
||||
<a target="_blank" href="@($"/blog/essays/{essay.FileName}")">
|
||||
<a href="@($"/blog/essays/{essay.FileName}")">
|
||||
<div class="flex flex-row p-2 mx-1 rounded-lg hover:bg-gray-300">
|
||||
<div class="w-20">
|
||||
@(essay.PublishTime.ToString("MM月dd日"))
|
||||
@@ -51,13 +51,13 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private readonly List<IGrouping<DateTime, BlogEssay>> _essays = [];
|
||||
private readonly List<IGrouping<DateTimeOffset, BlogEssay>> _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));
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
@@ -12,47 +12,42 @@
|
||||
|
||||
<div class="flex flex-col py-8">
|
||||
<div>
|
||||
<h1 id="title" class="text-4xl">@(_essay!.Title)</h1>
|
||||
<div class="col-auto">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-6 pt-4 pb-2">
|
||||
<div class="flex flex-row gap-4">
|
||||
<div class="font-light">
|
||||
@(_essay!.PublishTime.ToString("yyyy-MM-dd"))
|
||||
<div class="flex flex-col items-center">
|
||||
<div>
|
||||
<h1 id="title" class="text-4xl">@(_essay!.Title)</h1>
|
||||
</div>
|
||||
|
||||
@foreach (string tag in _essay!.Tags)
|
||||
<div class="flex flex-row gap-4 py-2">
|
||||
@foreach (string tag in _essay!.Tags)
|
||||
{
|
||||
<div class="text-sky-500">
|
||||
<a href="/blog/tags/?tagName=@(UrlEncoder.Default.Encode(tag))">
|
||||
# @(tag)
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="font-light pb-1">
|
||||
发布于: @(_essay.PublishTime.ToString("yyyy年MM月dd日 HH:mm:ss"))
|
||||
</div>
|
||||
|
||||
@if (_essay.UpdateTime != _essay.PublishTime)
|
||||
{
|
||||
<div class="text-sky-500">
|
||||
<a href="/blog/tags/?tagName=@(UrlEncoder.Default.Encode(tag))">
|
||||
# @(tag)
|
||||
</a>
|
||||
<div class="font-light pb-1">
|
||||
更新于: @(_essay.UpdateTime.ToString("yyyy年MM月dd日 HH:mm:ss"))
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-6 pt-2 pb-4">
|
||||
<div class="font-light">
|
||||
总字数:@(_essay!.WordCount)字,预计阅读时间 @(_essay!.ReadTime)。
|
||||
<div class="font-light pb-1">
|
||||
总字数:@(_essay!.WordCount)字,预计阅读时间 @(_essay!.ReadTime)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3">
|
||||
<div class="col-span-3 md:col-span-2 flex flex-col gap-3">
|
||||
<div>
|
||||
@((MarkupString)_essay!.HtmlContent)
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<LicenseDisclaimer EssayFilename="@BlogKey"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-3 md:col-span-1">
|
||||
<div class="flex flex-col sticky top-0 px-8">
|
||||
<div class="flex flex-col sticky top-20 px-8 pt-20">
|
||||
<div>
|
||||
<h3 class="text-2xl">文章目录</h3>
|
||||
</div>
|
||||
@@ -93,8 +88,17 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-3 md:col-span-2 flex flex-col gap-3">
|
||||
<div>
|
||||
@((MarkupString)_essay!.HtmlContent)
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<LicenseDisclaimer EssayFilename="@BlogKey"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
97
YaeBlog/Components/Pages/Index.razor
Normal file
97
YaeBlog/Components/Pages/Index.razor
Normal file
@@ -0,0 +1,97 @@
|
||||
@page "/"
|
||||
@using YaeBlog.Abstraction
|
||||
@using YaeBlog.Models
|
||||
@inject IEssayContentService EssayContentInstance
|
||||
|
||||
<PageTitle>
|
||||
Ricardo's Index
|
||||
</PageTitle>
|
||||
|
||||
<div class="mx-14 lg:mx-20">
|
||||
<div class="grid grid-cols-3 py-4 lg:mx-20">
|
||||
<div class="col-span-3 md:col-span-1 lg:m-10">
|
||||
<img src="@Assets["images/avatar.png"]" alt="Ricardo's Avatar"
|
||||
class="h-auto max-w-full rounded-md border border-gray-400">
|
||||
</div>
|
||||
|
||||
<div class="col-span-3 md:col-span-2">
|
||||
<div class="flex flex-col gap-y-3 items-center md:items-start md:px-6">
|
||||
<div class="">
|
||||
<div class="text-3xl font-bold">初冬的朝阳</div>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<p class="text-lg">a.k.a jackfiled</p>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<p class="text-lg italic">世界很大,时间很长。</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-2">
|
||||
<a href="https://github.com/jackfiled" target="_blank">
|
||||
<div>
|
||||
<span class="fa-brands fa-github text-xl"></span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="https://git.rrricardo.top/jackfiled/" target="_blank">
|
||||
<div>
|
||||
<span class="gitea-icon text-xl"></span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="https://space.bilibili.com/378831522" target="_blank">
|
||||
<div>
|
||||
<span class="fa-brands fa-bilibili text-xl"></span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="https://xhslink.com/m/5GVDzyKf3De" target="_blank">
|
||||
<div>
|
||||
<span class="rednote-icon text-xl"></span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="py-5">
|
||||
<p class="text-lg">恕我不能亲自为您沏茶,还是非常欢迎您来,能在广阔的互联网世界中发现这里实属不易。</p>
|
||||
</div>
|
||||
|
||||
<div class="text-lg pt-2">
|
||||
<p class="py-1">
|
||||
正在攻读计算机科学与技术的硕士学位,研究方向是AI编译和异构编译!
|
||||
</p>
|
||||
<p class="py-1">
|
||||
喜欢优雅的代码,香甜的蛋糕等等一切可爱的事物。
|
||||
</p>
|
||||
<p class="py-1">
|
||||
<Anchor Address="/blog/" Text="个人博客"/>中收集了我的各种奇思妙想,如果感兴趣欢迎移步。
|
||||
@if (_latestEssay is not null)
|
||||
{
|
||||
<span>
|
||||
最新的一期博客关注 <Anchor Text="@(_latestEssay.Title)" Address="@(_latestEssay.EssayLink)"/>。
|
||||
</span>
|
||||
}
|
||||
</p>
|
||||
<p class="py-1">
|
||||
日常的代码开发使用自建的<Anchor Text="Gitea" Address="https://git.rrricardo.top" NewPage="@(true)"/>进行,个人
|
||||
开发的各种项目都可以在上面找到。
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private BlogEssay? _latestEssay;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
_latestEssay = EssayContentInstance.Essays.OrderByDescending(e => e.UpdateTime).FirstOrDefault();
|
||||
}
|
||||
|
||||
}
|
||||
47
YaeBlog/Components/Pages/Index.razor.css
Normal file
47
YaeBlog/Components/Pages/Index.razor.css
Normal file
@@ -0,0 +1,47 @@
|
||||
.fa-brands {
|
||||
font-family: "Font Awesome 7 Brands", "Font Awesome 7 Free";
|
||||
font-style: normal;
|
||||
font-synthesis: none;
|
||||
font-variant: normal;
|
||||
line-height: 1;
|
||||
text-rendering: auto;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.fa-github::before {
|
||||
content: "\f09b";
|
||||
color: #24292e;
|
||||
}
|
||||
|
||||
.fa-bilibili::before {
|
||||
content: "\e3d9";
|
||||
color: #00AEEC;
|
||||
}
|
||||
|
||||
.gitea-icon {
|
||||
display: inline-block;
|
||||
vertical-align: -0.125em;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
||||
background-image: url("https://docs.gitea.com/img/gitea.svg");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.rednote-icon {
|
||||
display: inline-block;
|
||||
vertical-align: -0.125em;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
||||
background-image: url("images/xiaohongshu-seeklogo.svg");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
@@ -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
|
||||
53
YaeBlog/Directory.Build.targets
Normal file
53
YaeBlog/Directory.Build.targets
Normal file
@@ -0,0 +1,53 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ClientAssetsRestoreCommand Condition="'$(ClientAssesRestoreCommand)' == ''">pnpm install</ClientAssetsRestoreCommand>
|
||||
<ClientAssetsBuildCommand Condition="'$(ClientAssetsBuildCommand)' == ''">pnpm run build</ClientAssetsBuildCommand>
|
||||
<ClientAssetsBuildOutputParameter Condition="'$(ClientAssetsBuildOutputParameter)' == ''">--output</ClientAssetsBuildOutputParameter>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_RestoreClientAssetsBeforeTargets Condition="'$(TargetFramework)' == ''">DispatchToInnerBuilds</_RestoreClientAssetsBeforeTargets>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="RestoreClientAssets" BeforeTargets="$(_RestoreClientAssetsBeforeTargets)">
|
||||
<Message Importance="high" Text="Running $(ClientAssetsRestoreCommand)"/>
|
||||
<Exec Command="$(ClientAssetsRestoreCommand)"/>
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildClientAssets" DependsOnTargets="RestoreClientAssets" BeforeTargets="AssignTargetPaths">
|
||||
<PropertyGroup>
|
||||
<_ClientAssetsOutputFullPath>$([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)ClientAssets'))</_ClientAssetsOutputFullPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<MakeDir Directories="$(_ClientAssetsOutputFullPath)"/>
|
||||
<Exec Command="$(ClientAssetsBuildCommand) $(ClientAssetsBuildOutputParameter) $(_ClientAssetsOutputFullPath)"/>
|
||||
|
||||
<ItemGroup>
|
||||
<_ClientAssetsBuildOutput Include="$(IntermediateOutputPath)ClientAssets\**"/>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="DefineClientAssets" AfterTargets="BuildClientAssets" DependsOnTargets="ResolveStaticWebAssetsConfiguration">
|
||||
<ItemGroup>
|
||||
<FileWrites Include="@(_ClientAssetsBuildOutput)"/>
|
||||
</ItemGroup>
|
||||
|
||||
<DefineStaticWebAssets
|
||||
CandidateAssets="@(_ClientAssetsBuildOutput)"
|
||||
SourceId="$(PackageId)"
|
||||
SourceType="Computed"
|
||||
ContentRoot="$(_ClientAssetsOutputFullPath)"
|
||||
BasePath="$(StaticWebAssetBasePath)"
|
||||
>
|
||||
<Output TaskParameter="Assets" ItemName="StaticWebAsset"/>
|
||||
<Output TaskParameter="Assets" ItemName="_ClientAssetsStaticWebAsset"/>
|
||||
</DefineStaticWebAssets>
|
||||
|
||||
<DefineStaticWebAssetEndpoints
|
||||
CandidateAssets="@(_ClientAssetsStaticWebAsset)"
|
||||
ContentTypeMappings="@(StaticWebAssetContentTypeMapping)"
|
||||
>
|
||||
<Output TaskParameter="Endpoints" ItemName="StaticWebAssetEndpoint" />
|
||||
</DefineStaticWebAssetEndpoints>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,7 +1,10 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0
|
||||
FROM mcr.azure.cn/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 .
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
@attribute [StreamRendering]
|
||||
|
||||
<main class="container mx-auto flex flex-col min-h-screen">
|
||||
<div class="grid grid-cols-3 mx-3">
|
||||
<div class="md:col-span-2 col-span-3 h-20 flex items-center">
|
||||
<a href="/blog/">
|
||||
<span class="text-blue-600 text-2xl">Ricardo's Blog</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-1 col-span-3 h-20 flex items-center">
|
||||
<div class="flex flex-row w-full px-2 gap-3 md:justify-center justify-end">
|
||||
<div>
|
||||
<a href="/blog/archives/">
|
||||
<span class="text-xl text-blue-600">归档</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="/blog/tags/">
|
||||
<span class="text-xl text-blue-600">标签</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="/about/" target="_blank">
|
||||
<span class="text-xl text-blue-600">关于</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="/friends/" target="_blank">
|
||||
<span class="text-xl text-blue-600">友链</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-4 py-2 flex-grow">
|
||||
@Body
|
||||
</div>
|
||||
|
||||
<Foonter/>
|
||||
</main>
|
||||
@@ -6,10 +6,7 @@ namespace YaeBlog.Models;
|
||||
public record BlogContents(ConcurrentBag<BlogContent> Drafts, ConcurrentBag<BlogContent> Posts)
|
||||
: IEnumerable<BlogContent>
|
||||
{
|
||||
IEnumerator<BlogContent> IEnumerable<BlogContent>.GetEnumerator()
|
||||
{
|
||||
return Posts.Concat(Drafts).GetEnumerator();
|
||||
}
|
||||
public IEnumerator<BlogContent> GetEnumerator() => Posts.Concat(Drafts).GetEnumerator();
|
||||
|
||||
public IEnumerator GetEnumerator() => ((IEnumerable<BlogContent>)this).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ public class BlogEssay : IComparable<BlogEssay>
|
||||
|
||||
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; }
|
||||
|
||||
@@ -20,6 +22,8 @@ public class BlogEssay : IComparable<BlogEssay>
|
||||
|
||||
public required string HtmlContent { get; init; }
|
||||
|
||||
public string EssayLink => $"/blog/essays/{FileName}";
|
||||
|
||||
public BlogEssay WithNewHtmlContent(string newHtmlContent)
|
||||
{
|
||||
var essay = new BlogEssay
|
||||
@@ -28,6 +32,7 @@ public class BlogEssay : IComparable<BlogEssay>
|
||||
FileName = FileName,
|
||||
IsDraft = IsDraft,
|
||||
PublishTime = PublishTime,
|
||||
UpdateTime = UpdateTime,
|
||||
Description = Description,
|
||||
WordCount = WordCount,
|
||||
ReadTime = ReadTime,
|
||||
|
||||
@@ -4,7 +4,9 @@ public class MarkdownMetadata
|
||||
{
|
||||
public string? Title { get; set; }
|
||||
|
||||
public DateTime? Date { get; set; }
|
||||
public string? Date { get; set; }
|
||||
|
||||
public string? UpdateTime { get; set; }
|
||||
|
||||
public List<string>? Tags { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
@page "/about"
|
||||
|
||||
<PageTitle>
|
||||
关于
|
||||
</PageTitle>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div>
|
||||
<h1 class="text-4xl">关于</h1>
|
||||
</div>
|
||||
|
||||
<div class="py-4">
|
||||
<span class="italic">把字刻在石头上!(・’ω’・)</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col p-2">
|
||||
<div class="flex flex-col p-2">
|
||||
<div class="pb-2">
|
||||
<h3 class="text-2xl">关于我</h3>
|
||||
</div>
|
||||
|
||||
<div class="py-2">
|
||||
计算机科学与技术在读大学生,明光村幼儿园附属大学所属。正处于读书和失业的叠加态。
|
||||
一般在互联网上使用<span class="italic">初冬的朝阳</span>或者<span class="italic">jackfiled</span>的名字活动。
|
||||
<span class="line-through">都是ICP备案过的人了,网名似乎没有太大的用处(</span>
|
||||
</div>
|
||||
|
||||
<div class="py-2">
|
||||
主要是一个C#程序员,目前也在尝试写一点Rust。
|
||||
总体上对于编程语言的态度是“<span>大家都是我的翅膀.jpg</span>”。
|
||||
前后端分离的项目本当上手。
|
||||
常常因为现实的压力而写一些C/C++。
|
||||
<span class="line-through">对于Java和Go的评价很低。</span>
|
||||
日常使用ArchLinux。
|
||||
</div>
|
||||
|
||||
<div class="py-2">
|
||||
100%社恐。日常生活是宅在电脑前面自言自语。
|
||||
兴趣活动是读书和看番,目前在玩原神和三角洲。
|
||||
</div>
|
||||
|
||||
<div class="py-4">
|
||||
常常被人批评没有梦想,这里就随便瞎编一下。
|
||||
成为嵌入式工程师,修好桌面上的<a href="https://www.bilibili.com/video/BV1VA411p7MD">HoloCubic</a>。
|
||||
完成第一个不是课程设计的个人开源项目。
|
||||
遇到能够搭伙过日子的人也算是一大梦想,虽然社恐人根本不知道从何开始的说,
|
||||
<span class="line-through">什么时候天上才能掉美少女?</span>
|
||||
</div>
|
||||
|
||||
<div class="py-2">
|
||||
公开的联系渠道是<a href="mailto:shicangjuner@outlook.com">电子邮件</a>。
|
||||
也可以试试在各大平台搜索上面提到的名字。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col p-2">
|
||||
<div class="pb-2">
|
||||
<h3 class="text-2xl">关于本站</h3>
|
||||
</div>
|
||||
|
||||
<div class="py-2">
|
||||
本站肇始于2021年下半年,在开始的两年中个人网站和博客是分别的两个网站,个人网站是裸HTML写的,博客是用
|
||||
<a href="https://hexo.io">Hexo</a>渲染的。
|
||||
</div>
|
||||
|
||||
<div class="py-2">
|
||||
2024年,我们决定使用.NET技术完全重构两个网站,合二为一。虽然目前这个版本还是一个半成品,但是我们一定会努力的~(确信。
|
||||
</div>
|
||||
|
||||
<div class="py-2">
|
||||
2025年,我们将使用的样式库从Bootstrap迁移到Tailwind CSS,将现代的前端技术同Blazor结合起来。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
@page "/"
|
||||
|
||||
<PageTitle>
|
||||
Ricardo's Index
|
||||
</PageTitle>
|
||||
|
||||
<div class="mx-20">
|
||||
<div class="grid grid-cols-3 py-4">
|
||||
<div class="col-span-3 md:col-span-1 p-5 p-lg-0">
|
||||
<img src="images/avatar.png" alt="Ricardo's Avatar" class="h-auto max-w-full">
|
||||
</div>
|
||||
|
||||
<div class="col-span-3 md:col-span-2">
|
||||
<div class="flex flex-col px-3 gap-y-3">
|
||||
<div class="">
|
||||
<div class="text-3xl font-bold">初冬的朝阳 (Ricardo Ren)</div>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<p class="text-lg">a.k.a jackfiled</p>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<p class="text-lg italic">世界很大,时间很长。</p>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<p class="text-lg">
|
||||
平平无奇的计算机科学与技术学徒,连微小的贡献都没做。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="py-5">
|
||||
<p class="text-lg">恕我不能亲自为您沏茶(?),还是非常欢迎您能够来到我的主页。</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-lg py-1">
|
||||
如果您想四处看看,了解一下屏幕对面的人,可以在我的 <Anchor Address="/blog/" Text="博客"/> 看看。
|
||||
如果您对于明光村幼儿园某附属技校的计算机教学感兴趣,您可以移步到
|
||||
<Anchor Address="https://jackfiled.github.io/wiki/" Text="我的学习笔记"/>,
|
||||
<span class="fs-5 text-decoration-line-through">虽然这笔记我自己也木有看过。</span>
|
||||
如果您想批判一下我的代码,在
|
||||
<Anchor Address="https://github.com/jackfiled/" Text="Github"/> 和
|
||||
<Anchor Address="https://git.rrricardo.top/jackfiled/" Text="Gitea"/>
|
||||
都可以找到。
|
||||
</p>
|
||||
<p class="text-lg py-1">
|
||||
如果您真的很闲,也可以四处搜寻一下,也许存在着一些不为人知的彩蛋。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
@@ -16,8 +16,7 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor
|
||||
public async Task<BlogEssay> 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)
|
||||
@@ -102,17 +102,33 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 美化各种列表元素
|
||||
/// </summary>
|
||||
/// <param name="document"></param>
|
||||
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)
|
||||
{
|
||||
// 首先给<ul>元素添加样式
|
||||
ulElement.ClassList.Add("list-disc ml-10");
|
||||
// 给有序或者无序列表添加不同的样式
|
||||
listElement.ClassList.Add("ml-10");
|
||||
switch (listElement.LocalName)
|
||||
{
|
||||
case "ul":
|
||||
{
|
||||
listElement.ClassList.Add("list-disc");
|
||||
break;
|
||||
}
|
||||
case "ol":
|
||||
{
|
||||
listElement.ClassList.Add("list-decimal");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (IElement liElement in from e in ulElement.Children
|
||||
foreach (IElement liElement in from e in listElement.Children
|
||||
where e.LocalName == "li"
|
||||
select e)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<MarkdownMetadata>(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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
62
YaeBlog/Services/MarkdownWordCounter.cs
Normal file
62
YaeBlog/Services/MarkdownWordCounter.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ using YaeBlog.Models;
|
||||
|
||||
namespace YaeBlog.Services;
|
||||
|
||||
public partial class RendererService(
|
||||
public sealed partial class RendererService(
|
||||
ILogger<RendererService> logger,
|
||||
IEssayScanService essayScanService,
|
||||
MarkdownPipeline markdownPipeline,
|
||||
@@ -38,7 +38,15 @@ public partial class RendererService(
|
||||
List<BlogEssay> essays = [];
|
||||
foreach (BlogContent content in preProcessedContents)
|
||||
{
|
||||
uint wordCount = GetWordCount(content);
|
||||
(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,
|
||||
@@ -46,8 +54,9 @@ public partial class RendererService(
|
||||
IsDraft = content.IsDraft,
|
||||
Description = GetDescription(content),
|
||||
WordCount = wordCount,
|
||||
ReadTime = CalculateReadTime(wordCount),
|
||||
PublishTime = content.Metadata.Date ?? DateTime.Now,
|
||||
ReadTime = readTime,
|
||||
PublishTime = publishDate,
|
||||
UpdateTime = updateTime,
|
||||
HtmlContent = content.Content
|
||||
};
|
||||
|
||||
@@ -182,28 +191,21 @@ 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;
|
||||
}
|
||||
|
||||
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 {} is {}", content.BlogName,
|
||||
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'秒'"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,30 +11,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="EnsurePnpmInstalled" BeforeTargets="BeforeBuild">
|
||||
<Message Importance="low" Text="Ensure pnpm is installed..."/>
|
||||
<Exec Command="pnpm --version" ContinueOnError="true">
|
||||
<Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
|
||||
</Exec>
|
||||
|
||||
<Error Condition="$(ErrorCode) != 0" Text="Pnpm is not installed which is required for build."/>
|
||||
|
||||
<Message Importance="normal" Text="Installing pakages using pnpm..."/>
|
||||
<Exec Command="pnpm install"/>
|
||||
</Target>
|
||||
|
||||
<Target Name="TailwindGenerate" AfterTargets="EnsurePnpmInstalled" BeforeTargets="BeforeBuild" Condition="'$(_IsPublishing)' == 'yes'">
|
||||
<Message Importance="normal" Text="Generate css files using tailwind..."/>
|
||||
<Exec Command="pnpm tailwindcss -i wwwroot/tailwind.css -o $(IntermediateOutputPath)tailwind.g.css"/>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(IntermediateOutputPath)tailwind.g.css" Visible="false" TargetPath="wwwroot/tailwind.g.css"/>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<PropertyGroup>
|
||||
<ClientAssetsRestoreCommand>pnpm install</ClientAssetsRestoreCommand>
|
||||
<ClientAssetsBuildCommand>pwsh build.ps1 tailwind</ClientAssetsBuildCommand>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -21,12 +21,6 @@
|
||||
"Link": "https://ichirinko.top",
|
||||
"AvatarImage": "https://ichirinko-blog-img-1.oss-cn-shenzhen.aliyuncs.com/Pic_res/img/202209122110798.png"
|
||||
},
|
||||
{
|
||||
"Name": "志田千陽",
|
||||
"Description": "日出多值得",
|
||||
"Link": "https://zzachary.top/",
|
||||
"AvatarImage": "https://zzachary.top/img/ztqy_hub928259802d192ff5718c06370f0f2c4_48203_300x0_resize_q75_box.jpg"
|
||||
},
|
||||
{
|
||||
"Name": "不会写程序的晨旭",
|
||||
"Description": "一个普通大学生",
|
||||
@@ -37,7 +31,7 @@
|
||||
"Name": "万木长风",
|
||||
"Description": "世界渲染中...",
|
||||
"Link": "https://ryohai.fun",
|
||||
"AvatarImage": "https://ryohai.fun/icon.jpg"
|
||||
"AvatarImage": "https://ryohai.fun/static/favicons/favicon-32x32.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
124
YaeBlog/build.ps1
Executable file
124
YaeBlog/build.ps1
Executable file
@@ -0,0 +1,124 @@
|
||||
#!pwsh
|
||||
|
||||
[cmdletbinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, Position = 0, HelpMessage = "Specify the build target")]
|
||||
[ValidateSet("tailwind", "publish", "compress", "build", "dev", "new")]
|
||||
[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
|
||||
}
|
||||
}
|
||||
|
||||
if ($Target -eq "new")
|
||||
{
|
||||
if ($Essay -eq "")
|
||||
{
|
||||
Write-Error "No new name, 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
|
||||
}
|
||||
|
||||
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" {
|
||||
Write-Host "Build tailwind css into $Output."
|
||||
pnpm tailwindcss -i wwwroot/tailwind.css -o $Output/tailwind.g.css
|
||||
break
|
||||
}
|
||||
"publish" {
|
||||
Write-Host "Publish essay $Essay..."
|
||||
dotnet run -- publish $Essay
|
||||
|
||||
if ($Compress)
|
||||
{
|
||||
Compress-Image
|
||||
}
|
||||
break
|
||||
}
|
||||
"compress" {
|
||||
Compress-Image
|
||||
break
|
||||
}
|
||||
"build" {
|
||||
Build-Image
|
||||
break
|
||||
}
|
||||
"dev" {
|
||||
Start-Develop
|
||||
break
|
||||
}
|
||||
"new" {
|
||||
dotnet run -- new $Essay
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
224
YaeBlog/source/posts/aspnetcore-swa.md
Normal file
224
YaeBlog/source/posts/aspnetcore-swa.md
Normal file
@@ -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中。
|
||||
|
||||
<!--more-->
|
||||
|
||||
### 引言——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": "<Project>/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)生成的两个包:
|
||||
|
||||
- `<<Project>>.styles.css` 是 **Root** 资源,仅在作为主项目时使用。
|
||||
- `<<Project>>.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:
|
||||
|
||||

|
||||
|
||||
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
|
||||
<PropertyGroup>
|
||||
<ClientAssetsRestoreCommand Condition="'$(ClientAssesRestoreCommand)' == ''">pnpm install</ClientAssetsRestoreCommand>
|
||||
<ClientAssetsBuildCommand Condition="'$(ClientAssetsBuildCommand)' == ''">pnpm run build</ClientAssetsBuildCommand>
|
||||
<ClientAssetsBuildOutputParameter Condition="'$(ClientAssetsBuildOutputParameter)' == ''">--output</ClientAssetsBuildOutputParameter>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
最终就是完成的构建原始代码了。第一个运行的构建目标`RestoreClientAssets`将会在`DispatchToInnerBuilds`任务运行之后运行,这个目标是MSBuild构建管线中一个不论是针对单架构生成还是多架构生成都只会运行一次的目标,这样在项目需要同时编译到.NET 8和.NET 10的情况下,仍然只会运行前端的安装命令一次。`BuildClientAssets`目标紧接着`RestoreClientAssets`目标的运行而运行,并将所有生成的前端文件添加到`_ClientAssetsBuildOutput`项中。最终的`DefineClientAssets`目标在负责解析项目中的所有静态文件的目标`ResolveWebAssetsConfiguration`运行之前运行,调用`DefineStaticWebAssets`和`DefineStaticWebAssetEndpoints`将前面生成的所有前端静态文件添加到MSBuild的静态文件处理管线中。
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<_RestoreClientAssetsBeforeTargets Condition="'$(TargetFramework)' == ''">DispatchToInnerBuilds</_RestoreClientAssetsBeforeTargets>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="RestoreClientAssets" BeforeTargets="$(_RestoreClientAssetsBeforeTargets)">
|
||||
<Message Importance="high" Text="Running $(ClientAssetsRestoreCommand)"/>
|
||||
<Exec Command="$(ClientAssetsRestoreCommand)"/>
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildClientAssets" DependsOnTargets="RestoreClientAssets" BeforeTargets="AssignTargetPaths">
|
||||
<PropertyGroup>
|
||||
<_ClientAssetsOutputFullPath>$([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)ClientAssets'))</_ClientAssetsOutputFullPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<MakeDir Directories="$(_ClientAssetsOutputFullPath"/>
|
||||
<Exec Command="$(ClientAssetsBuildCommand) -- $(ClientAssetsBuildOutputParameter) $(_ClientAssetsOutputFullPath)"/>
|
||||
|
||||
<ItemGroup>
|
||||
<_ClientAssetsBuildOutput Include="$(IntermediateOutputPath)ClientAssets\**"/>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="DefineClientAssets" AfterTargets="BuildClientAssets" DependsOnTargets="ResolveWebAssetsConfiguration">
|
||||
<ItemGroup>
|
||||
<FileWrites Include="@(_ClientAssetsBuildOutput)"/>
|
||||
</ItemGroup>
|
||||
|
||||
<DefineStaticWebAssets
|
||||
CandidateAssets="@(_ClientAssetsBuildOutput)"
|
||||
SourceId="$(PackageId)"
|
||||
SourceType="Computed"
|
||||
ContentRoot="$(_ClientAssetsOutputFullPath)"
|
||||
BasePath="$(StaticWebAssetBasePath)"
|
||||
>
|
||||
<Output TaskParameter="Assets" ItemName="StaticWebAsset"/>
|
||||
<Output TaskParameter="Assets" ItemName="_ClientAssetsStaticWebAsset"/>
|
||||
</DefineStaticWebAssets>
|
||||
|
||||
<DefineStaticWebAssetEndpoints
|
||||
CandidateAssets="@(_ClientAssetsStaticWebAsset)"
|
||||
ContentTypeMappings="@(StaticWebAssetContentTypeMapping)"
|
||||
>
|
||||
<Output TaskParameter="Endpoints" ItemName="StaticWebAssetEndpoint" />
|
||||
</DefineStaticWebAssetEndpoints>
|
||||
</Target>
|
||||
```
|
||||
|
||||
为了测试如下的代码,可以在项目中新建一个`Directory.Build.targets`文件,将上述的内容复制进去进行测试,当然别忘了用`<Project>`标签包裹这一切。
|
||||
BIN
YaeBlog/source/posts/aspnetcore-swa/image-20251231225433184.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/aspnetcore-swa/image-20251231225433184.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
370
YaeBlog/source/posts/hpc-2025-cuda.md
Normal file
370
YaeBlog/source/posts/hpc-2025-cuda.md
Normal file
@@ -0,0 +1,370 @@
|
||||
---
|
||||
title: High Performance Computing 25 SP NVIDIA
|
||||
date: 2025-08-31T13:50:42.8639950+08:00
|
||||
tags:
|
||||
- 高性能计算
|
||||
- 学习资料
|
||||
---
|
||||
|
||||
|
||||
|
||||
Fxxk you, NVIDIA!
|
||||
|
||||
<!--more-->
|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
**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
|
||||
|
||||

|
||||
|
||||
### 2010: Fermi GF100 GPU
|
||||
|
||||
**Fermi SM**:
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
### 2014 Maxwell
|
||||
|
||||
4 GPCs and 16 SMM.
|
||||
|
||||

|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
### 2022 Hopper
|
||||
|
||||
Introduce the GH200 Grace Hopper Superchip:
|
||||
|
||||

|
||||
|
||||
A system contains a CPU and GPU which is linked by a NVLink technology.
|
||||
|
||||
And this system can scale out for machine learning.
|
||||
|
||||

|
||||
|
||||
Memory access across the NVLink:
|
||||
|
||||
- GPU to local CPU
|
||||
- GPU to peer GPU
|
||||
- GPU to peer CPU
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
### 2025 Blackwell
|
||||
|
||||

|
||||
|
||||
### 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.
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
**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.
|
||||
|
||||

|
||||
|
||||
**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:
|
||||
|
||||

|
||||
|
||||
And provides dual **Streaming Data Transfer** engines to enable streaming data transfer, a.k.a direct memory access.
|
||||
|
||||

|
||||
|
||||
### GPUDirect
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
|
||||

|
||||
|
||||
**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.
|
||||
|
||||

|
||||
|
||||
Batching via dynamic parallelism:
|
||||
|
||||
- Move top-level loops to GPUs.
|
||||
- Run thousands of independent tasks.
|
||||
- Release CPU for other work.
|
||||
|
||||

|
||||
|
||||
### Grid Management Unit
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
With Hyper-Q:
|
||||
|
||||

|
||||
|
||||
In pascal, **asynchronous concurrent computing** is introduced.
|
||||
|
||||

|
||||
|
||||
### NVLink: High-Speed Node Network
|
||||
|
||||

|
||||
|
||||
> 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.
|
||||
|
||||

|
||||
|
||||
### Tensor Core
|
||||
|
||||
Operates on a 4x4 matrix and performs: D = A x B + C.
|
||||
|
||||

|
||||
|
||||
### GPU Multi-Process Scheduling
|
||||
|
||||
- Timeslice scheduling: single process throughput optimization.
|
||||
- Multi process service: multi-process throughput optimization.
|
||||
|
||||
How about multi-process time slicing:
|
||||
|
||||

|
||||
|
||||
Volta introduces the multi-process services:
|
||||
|
||||

|
||||
|
||||
|
||||
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424192311202.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424192311202.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424192825010.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424192825010.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424193125125.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424193125125.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424195111341.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424195111341.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424195221886.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424195221886.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424200022880.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424200022880.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424200330783.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250424200330783.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508183446257.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508183446257.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508183528381.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508183528381.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508183724162.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508183724162.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508183931464.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508183931464.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508184155087.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508184155087.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508184455215.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508184455215.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508185236920.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508185236920.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508185812302.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508185812302.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508193756274.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508193756274.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508194350572.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508194350572.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508195840957.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508195840957.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508195938546.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508195938546.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508200041910.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250508200041910.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515183524043.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515183524043.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515184225475.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515184225475.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515184621914.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515184621914.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515184714663.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515184714663.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515185019590.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515185019590.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515185034758.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515185034758.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515185212184.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515185212184.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515185801775.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515185801775.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515190244112.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515190244112.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515190507199.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515190507199.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515190703918.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515190703918.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515191142384.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-cuda/image-20250515191142384.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
224
YaeBlog/source/posts/hpc-2025-distributed-system.md
Normal file
224
YaeBlog/source/posts/hpc-2025-distributed-system.md
Normal file
@@ -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.
|
||||
|
||||
<!--more-->
|
||||
|
||||
### 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.
|
||||
|
||||

|
||||
|
||||
**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:
|
||||
|
||||

|
||||
|
||||
**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.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
**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.
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
|
||||
**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:
|
||||
|
||||

|
||||
|
||||
Type 2 hypervisor:
|
||||
|
||||

|
||||
|
||||
**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.
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250410193527994.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250410193527994.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417184421464.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417184421464.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417185200176.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417185200176.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417190247790.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417190247790.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191509682.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191509682.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191526416.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417191526416.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417192453944.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250417192453944.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183610157.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183610157.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183629681.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183629681.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183645210.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-distributed-system/image-20250424183645210.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
80
YaeBlog/source/posts/hpc-2025-heterogeneous-system.md
Normal file
80
YaeBlog/source/posts/hpc-2025-heterogeneous-system.md
Normal file
@@ -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!
|
||||
|
||||
<!--more-->
|
||||
|
||||
## GPU Computing Ecosystem
|
||||
|
||||
CUDA: NVIDIA's Architecture for GPU computing.
|
||||
|
||||

|
||||
|
||||
## 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**
|
||||
|
||||

|
||||
|
||||
**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
|
||||
|
||||

|
||||
|
||||
#### First Stage: Intel Northbrige
|
||||
|
||||

|
||||
|
||||
### Second Stage: Symmetric Multiprocessors:
|
||||
|
||||

|
||||
|
||||
### Third Stage: Nonuniform Memory Access
|
||||
|
||||
And the memory controller is integrated directly in the CPU.
|
||||
|
||||

|
||||
|
||||
So in such context, the multiple CPUs is called NUMA:
|
||||
|
||||

|
||||
|
||||
And so there can be multi GPUs:
|
||||
|
||||

|
||||
|
||||
### Fourth Stage: Integrated PCIe in CPU
|
||||
|
||||

|
||||
|
||||
And there is such team *integrated CPU*, which integrated a GPU into the CPU chipset.
|
||||
|
||||

|
||||
|
||||
And the integrated GPU can work with discrete GPUs:
|
||||
|
||||

|
||||
|
||||
### Final Stage: Multi GPU Board
|
||||
|
||||

|
||||
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417195644624.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417195644624.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417200241703.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250417200241703.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424184701573.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424184701573.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185022360.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185022360.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185048036.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185048036.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185152081.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185152081.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185219673.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185219673.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185322963.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185322963.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185354247.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185354247.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185449577.webp
(Stored with Git LFS)
Normal file
BIN
YaeBlog/source/posts/hpc-2025-heterogeneous-system/image-20250424185449577.webp
(Stored with Git LFS)
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user