hez2010の日常

kekekekekekeke

新しい関数ポインタの機能を試しましょう

はじめに .NET 5には新しい機能「関数ポインタ」を追加されました。これは、ネイティブ相互運用を改善し、パフォーマンスを上げるために作られました。 関数ポインタは下位レベルのデリゲートで、関数呼び出しのより効率的な方法を提供します。既知の方法にSystem.Action<>, System.Func<>といい、System.Delegateといい、どちらもオーバーヘッドがたくさんあります。でも関数ポインタはそういうオーバーエンドがありません。 さらに関数ポインタはマネージ関数のみをサポートすることではなく、アンマネージ関数もサポートされています。だからこそ、ネイティブ相互運用を行う場合は、関数ポインタを使用すると非常に便利になります。 じゃ、早速始めましょう。 使い方 関数ポインタの型の形式は delegate* + managed/unmanaged + [呼び出し規則(ネイティブのみ)]+<パラメータリスト(void を使えます)>です。例えば: 言語宣言関数ポインタの型C++void __cdecl f()delegate* unmanaged[Cdecl]<void>C++void __cdecl f(int)delegate* unmanaged[Cdecl]<int, void>C++int __cdecl f(int)delegate* unmanaged[Cdecl]<int, int>C#static string f(int)delegate* managed<int, string>C#static void f(string)delegate* managed<string, void> [Cdecl] のみではなく、[Stdcall]と[Fastcall]と[Thiscall]も使えますよ。 アンマネージ関数の使い方 まずはネイティブ相互運用を紹介しましょう。 C++コードを書けます: #define WIN32 #define UNICODE #include <cstring> #include <cstdio> extern “C” __declspec(dllexport) // C# 関数はパラメーター gen […]

Read More

新版 C# 高效率编程指南

前言 C# 从 7 版本开始一直到如今的 9 版本,加入了非常多的特性,其中不乏改善性能、增加程序健壮性和代码简洁性、可读性的改进,这里我整理一些使用新版 C# 的时候个人推荐的写法,可能不适用于所有的人,但是还是希望对你们有所帮助。 注意:本指南适用于 .NET 5 或以上版本。 使用 ref struct 做到 0 GC C# 7 开始引入了一种叫做 ref struct 的结构,这种结构本质是 struct ,结构存储在栈内存。但是与 struct 不同的是,该结构不允许实现任何接口,并由编译器保证该结构永远不会被装箱,因此不会给 GC 带来任何的压力。相对的,使用中就会有不能逃逸出栈的强制限制。 Span<T> 就是利用 ref struct 的产物,成功的封装出了安全且高性能的内存访问操作,且可在大多数情况下代替指针而不损失任何的性能。 ref struct MyStruct { public int Value { get; set; } } class RefStructGuide { static void Test() { […]

Read More

.NET 异步解说

前言 要了解 .NET 中的 async/await 机制,首先需要有操作系统原理的基础,否则的话是很难理解清楚的,如果没有这些基础而试图向他人解释,大多也只是基于现象得到的错误猜想。 初看异步 说到异步大家应该都很熟悉了,2012 年 C# 5 引入了新的异步机制:Task,并且还有两个新的关键字 await 和 async,这已经不是什么新鲜事了,而且如今这个异步机制已经被各大语言借鉴,如 JavaScript、TypeScript、Rust、C++ 等等。 下面给出一个简单的对照: 语言调度单位关键字/方法C#Task<>、ValueTask<>async、awaitC++std::future<>co_awaitRuststd::future::Future<>.awaitJavaScript、TypeScriptPromise<>async、await 当然,这里这并不是本文的重点,只是提一下,方便大家在有其他语言经验的情况下(如果有),可以认识到 C# 中 Task 和 async/await 究竟是一个和什么可以相提并论的东西。 多线程编程 在该异步编程模型诞生之前,多线程编程模型是很多人所熟知的。一般来说,开发者会使用 Thread、std::thread 之类的东西作为线程的调度单位来进行多线程开发,每一个这样的结构表示一个对等线程,线程之间采用互斥或者信号量等方式进行同步。 多线程对于科学计算速度提升等方面效果显著,但是对于 IO 负荷的任务,例如从读取文件或者 TCP 流,大多数方案只是分配一个线程进行读取,读取过程中阻塞该线程: void Main() { while (true) { var client = socket.Accept(); new Thread(() => ClientThread(client)).Start(); } } void ClientThread(Socket client) { […]

Read More

手写一个简易的多周期 MIPS CPU

一点前言 多周期 CPU 相比单周期 CPU 以及流水线的实现来说其实写起来要麻烦那么一些,但是相对于流水线以及单周期 CPU 而言,多周期 CPU 除了能提升主频之外似乎并没有什么卵用。不过我的课题是多周期 CPU 那么就开始吧。 多周期 CPU 不同于单周期 CPU,多周期 CPU 指的是将整个 CPU 的执行过程分成几个阶段,每个阶段用一个时钟去完 成,然后开始下一条指令的执行,而每种指令执行时所用的时钟数不尽相同,这就是所谓的多周期CPU。 CPU在处理指令时,一般需要经过以下几个阶段: (1) 取指令(IF):根据程序计数器 PC 中的指令地址,从存储器中取出一条指令,同时,PC 根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令 时,则控制器把“转移地址”送入 PC,当然得到的“地址”需要做些变换才送入 PC。 (2) 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。 (3) 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。 (4) 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得 到数据地址单元中的数据。 (5) 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。 这也就意味着一条 CPU 指令最长需要 5 个时钟周期才能执行完毕,至于具体需要多少周期则根据指令的不同而不同。 MIPS 指令集的设计为定长简单指令集,这为 CPU 的实现带来了极大的方便。 指令集 MIPS 指令分为三种:R、I 和 J,三种指令有不同的存储方式: 其中, op:操作码;rs:第1个源操作数寄存器,寄存器地址(编号)是00000~11111,00~1F;rt:第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上);rd:目的操作数寄存器,寄存器地址(同上);sa:位移量(shift […]

Read More

你所不知道的 C# 中的细节

前言 有一个东西叫做鸭子类型,所谓鸭子类型就是,只要一个东西表现得像鸭子那么就能推出这玩意就是鸭子。 C# 里面其实也暗藏了很多类似“鸭子类型”的东西,但是很多开发者并不知道,因此也就没法好好利用这些东西,那么今天我细数一下这些藏在编译器中的细节。 至于为什么 C# 要这么做,其实有部分的原因是为了让不能实现接口的 ref struct 也能实现对应的功能。 不是只有 Task 和 ValueTask 才能 await 在 C# 中编写异步代码的时候,我们经常会选择将异步代码包含在一个 Task 或者 ValueTask 中,这样调用者就能用 await 的方式实现异步调用。 西卡西,并不是只有 Task 和 ValueTask 才能 await。Task 和 ValueTask 背后明明是由线程池参与调度的,可是为什么 C# 的 async/await 却被说成是 coroutine 呢? 因为你所 await 的东西不一定是 Task/ValueTask,在 C# 中只要你的类中包含 GetAwaiter() 方法和 bool IsCompleted 属性,并且 GetAwaiter() 返回的东西包含一个 GetResult() 方法、一个 […]

Read More

C# 9 新特性:代码生成器、编译时反射

前言 今天 .NET 官方博客宣布 C# 9 Source Generators 第一个预览版发布,这是一个用户已经喊了快 5 年特性,今天终于发布了。 简介 Source Generators 顾名思义代码生成器,它允许开发者在代码编译过程中获取查看用户代码并且生成新的 C# 代码参与编译过程,并且可以很好的与代码分析器集成提供 Intellisense、调试信息和报错信息,可以用它来做代码生成,因此也相当于是一个加强版本的编译时反射。 使用 Source Generators,可以做到这些事情: 获取一个 Compilation 对象,这个对象表示了所有正在编译的用户代码,你可以从中获取 AST 和语义模型等信息可以向 Compilation 对象中插入新的代码,让编译器连同已有的用户代码一起编译 Source Generators 作为编译过程中的一个阶段执行: 编译运行 -> [分析源代码 -> 生成新代码] -> 将生成的新代码添加入编译过程 -> 编译继续。 上述流程中,中括号包括的内容即为 Source Generators 所参与的阶段和能做到的事情。 作用 .NET 明明具备运行时反射和动态 IL 织入功能,那这个 Source Generators 有什么用呢? 编译时反射 – 0 […]

Read More

Hello world!

ようこそ、こちらは最初の投稿です。 Welcome! This is my first post.

Read More