知乎問題之:.NET AOT編譯後能替代C++嗎?

標題上的Native庫是指:Native分為靜態庫(

作者:nscript
鏈接://www.zhihu.com/question/536903224/answer/2522626086


 image

(1)開發 NDK 庫有人進行嘗試:NativeAOT-AndroidHelloJniLib (//github.com/josephmoresena/NativeAOT-AndroidHelloJniLib) 通過修改 .Net Runtime 來生成 Android 下的 NDK Native 庫。希望後續官方能直接支援。

(2)Remobjects Elements (//www.remobjects.com/elements/) 收費工具。可以採用 C# 語言開發 NDK Native 庫,限制是不能自由使用 Core CLR,而是使用 Remobjects 的平台相關庫 island 和跨平台庫 RTL,RTL 庫和 Core CLR 的相關類型介面相似。

Remobjects Elements 也可以用 C# 開發 wasm 模組,試用了下,滿小巧的,gzip 下可以做到低於 1M,性能測試大約是 .net 6 的1/4-1/5。同上面一樣,庫這塊會受到限制。

Remobjects Elements 的思路蠻有意思的,可以用 C#[或 Oxygene(Object Pascal), Swift, Java, Go or Mercury (VB)] 來寫各種庫或應用程式。Swift 語言的免費,其他的收費。可以用這些語言寫native 程式、 .net 程式、 jvm 程式 ……

----

補充一下,這是個很值得探討的問題,我一直在嘗試用 csharp 代替 cpp。很多地方可以直接替代掉,但也遇到了一些問題:

(1)寫底層演算法或庫的話,而調用者各種語言都有,csharp的,vb的,java 的,delphi 的都碰到過。那時的工作流程是,csharp 寫演算法,驗證之後,再根據對方需求,改成 cpp。有 AOT 之後,這種情況可以避免掉。

(2)移動開發:Android開發底層的庫,參考資料太少。比如,開發個 sdk,給 java 調用,xamarin 資料太少,解決方案也不通用。

AOT 工具鏈完善後,對我來說,可以解決很多問題:

(1)寫底層庫:可以完全放心大膽的用 csharp 寫底層庫了,jit 模式下快速開發,aot 模式發布。csharp 的生產力比 cpp 高太多,構建系統也比 cmake 好使的多。我現在正在嘗試優化 csharp 腳本開發 aot dll 的開發流程,目前還沒完全完成,但也挺舒服的,以一個最簡單的例子來說明,

lib.csx:

using System.Runtime.InteropServices;

[UnmanagedCallersOnly(EntryPoint = "add")]
public static int Add(int a, int b)
{
    return a + b;
}

[DllImport("pack/out/lib.dll")]
private static extern int add(int a, int b);

int result = add(2,4);
Console.WriteLine(result);

一行命令就編譯成 dll 了,win10下是964 k。

nscript pack --aot --lib lib.csx

再用下面指令調用腳本:

nscript .\lib.csx

輸出:

6

這種開發模式可比寫 cpp 舒服太多了。目前 aot 工具鏈還不夠完善,但是開發起來難度不大,就是工作量的問題。比如,根據程式碼,直接生成各個語言的調用封裝,生成文檔 ......

這樣一來,就可以用 csharp 快速開發出底層庫,生成動態鏈接庫、靜態鏈接庫給桌面用,給伺服器用,給移動設備用。底層庫一般不牽扯到ui,其通用性極強。做 android 的 ndk 開發太折騰,cpp 開發,jni 調用,調試麻煩得很。

這也可以解決 csharp 這塊防止反編譯的問題,發布時也可以 UI 層用 jit 跑,底層用 aot 跑來保護程式碼。

(2)開發小工具

我造過一個輪子,把 avalonia 的控制項庫的核心給拆出來,用 direct2d 來繪製,弄了一些基本的控制項,寫簡單的桌面程式,aot 之後尺寸可以控制在 4M,如果寫一些小工具還是可以的。

再比如說,如果想發布一些 server 端小工具,以 http 方式提供服務,jit 模式下,用 asp.net 來做,單個文件發布壓縮之後可以做到20-30M, 用 EmbedIO 來寫的話,單個文件發布,win下11M,linux 下 13M。EmbedIO用了反射,aot 我沒配好,沒跑出來,根據經驗的話,aot 應該可以做到 2-4 M。不過,11M 也夠用了,來看看用腳本寫的程式碼:

#r "nuget: EmbedIO, 3.4.3"

using EmbedIO;
using EmbedIO.WebApi;
using EmbedIO.Actions;
using EmbedIO.Files;

WebServer CreateWebServer(string url)
{
    var server = new WebServer(o => o
            .WithUrlPrefix(url)
            .WithMode(HttpListenerMode.EmbedIO))
        .WithLocalSessionManager()
        .WithStaticFolder("/static/", "./webroot", true, m => m.WithContentCaching(true)) // Add static files after other modules to avoid conflicts
        .WithModule(new ActionModule("/api", HttpVerbs.Get, HandleApi))
        .WithModule(new ActionModule("/", HttpVerbs.Any, ctx => ctx.SendDataAsync(new { Message = "Hello!" })))
        ;
    return server;
}

Task HandleApi(IHttpContext ctx)
{
    return ctx.SendDataAsync("api");
}

//@main
var url = "//*:5000/";
var server = CreateWebServer(url);
server.RunAsync();
Console.ReadKey();

所以,工具鏈完善後,用 csharp 寫東西很爽的:

(1)寫完之後,AOT一下,給各個語言用 <--> 對標 cpp;

(2)寫完之後,打包個幾M 的小東西,扔伺服器跑 <--> 是不是很像 golang?

(3)不建項目,像我上面那樣用腳本寫 <--> python;

(4)正常使用 <--> java;

(5)上 blazor <--> js。

除此之外,還有很新東西可以玩的 ......

----

一些場景替代不了:

(1)帶 gc 的 .net aot,最小尺寸差不多1M,對於有的場景這個尺寸過大。比如說,對於插件系統,每個插件1M,100個就100M,過大,並且全部載入的話,有100個gc在跑,浪費。不知有沒技術讓100個插件共享一個 gc。雖然可以不開gc,但這樣,就只是個大號的C了。

(2)對實時性要求高的場景,因為有gc存在會受到限制。

其他場景可以當有 gc 的 cpp 用,應該可以達到 cpp 80%+的性能。

其實現在jit,aot以及源生cpp性能都差不了太多,我做過密集計算的測試,細節忘記了,只記得是個 float 密集計算,java 的性能大於csharp和cpp。後來分析,jvm 做了向量化並行處理,csharp和cpp沒有做。改csharp程式碼手動用simd,最後性能略高於Java。

aot的除了啟動快,還有其他好處:(1)程式碼很難反編譯;(2)尺寸小。

假設你要寫個東西,給java調用或python調用,用 jit 的話,得幾十M,aot 的話,可以做到 1M。這樣的話,很多用cpp寫的sdk,可以用csharp來寫,aot編譯成動態鏈接庫,提供給上層用。

而不開gc的話,你可以就當他是個C語言的方言。

Windows以lib後綴名,Linux以a後綴 名)和動態庫(Windows以dll為後綴名,Linux以so後綴名。 本文順序先從生成Native程式.然後是動態庫,最後是靜態庫。

Tags: