Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
CysharpのOSS群から見るModern C#の現在地
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Yoshifumi Kawai
November 18, 2024
Technology
41k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
CysharpのOSS群から見るModern C#の現在地
Sansan x Cysharp
Yoshifumi Kawai
November 18, 2024
More Decks by Yoshifumi Kawai
See All by Yoshifumi Kawai
.NET for Android with Native AOT Built with Cursor
neuecc
0
680
R3のコードから見る実践LINQ実装最適化・コンカレントプログラミング実例
neuecc
5
56k
.NETの非同期戦略とUnityとの相互運用
neuecc
3
56k
他言語がメインの場合のRustの活用法 - csbindgenによるC# x Rust FFI実践事例
neuecc
7
41k
Modern High Performance C# 2023 Edition
neuecc
4
16k
CEDEC 2023 モダンハイパフォーマンスC# 2023 Edition
neuecc
9
120k
C#11 による世界最速バイナリシリアライザー「MemoryPack」の作り方
neuecc
1
42k
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
neuecc
1
500
Deep Dive async/await in Unity with UniTask(UniRx.Async)
neuecc
0
530
Other Decks in Technology
See All in Technology
RSA暗号を手計算したくなること、ありますよね?? (20260615_orestudy6_rsa)
thousanda
0
240
自宅LLMの話
jacopen
1
260
200個のGitHubリポジトリを横断調査したかった
icck
0
110
2026TECHFRESH畢業分享會 - AI 時代的人生存檔點
line_developers_tw
PRO
0
810
中期計画、2回作ってみた ~業務委託と正社員、両方の視点から~
demaecan
1
680
AIっぽい文章を採点して人間らしく直すアプリを作ってみた
yama3133
2
130
Chainlitで作るお手軽チャットUI
ynt0485
0
200
地球に⽣きるAI —GeoAIと「中間領域」— / AI Living on Earth — GeoAI and the “Intermediate Layer” —
ykiyota
0
280
爆速でマルチプロダクトを立ち上げる時 事業・CTO目線で大事にしたい事
miyatakoji
0
100
白金鉱業Meetup_Vol.24_「AIエージェントは分けるほど良い」は本当か? / Is it true that “the more you divide AI agents, the better”?
brainpadpr
1
300
"何を作るか"を任される エンジニアは、どう育つのか
yutaokafuji
1
600
小さく始める AI 活用推進 ― 日経電子版 Web チームの事例/nikkei-tech-talk47
nikkei_engineer_recruiting
0
220
Featured
See All Featured
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
390
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
480
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
1.1k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
0
1.7k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
220
Build your cross-platform service in a week with App Engine
jlugia
234
18k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.4k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.2k
Making the Leap to Tech Lead
cromwellryan
135
9.9k
Transcript
CysharpのOSSから見る Modern C#の現在地 2024-11-18 Cysharp x Sansan Event Yoshifumi Kawai
/ Cysharp, Inc.
About Speaker 河合 宜文 / Kawai Yoshifumi / @neuecc Cysharp,
Inc. - CEO/CTO 株式会社Cygamesの子会社として2018年9月設立 C#関連の研究開発/OSS/コンサルティングを行う Microsoft MVP for Developer Technologies(C#) since 2011 CEDEC AWARDS 2022エンジニアリング部門優秀賞 .NETのクラスライブラリ設計 改訂新版 監訳 50以上のOSSライブラリ開発(UniRx, UniTask, MessagePack C#, etc...) C#では世界でもトップレベルのGitHub Star(合計40000+)を獲得
https://github.com/Cysharp/ GitHubスター総数30000以上!
上位10個(合計27000スター)を 見ていきます! 技術的に見るべきところが多いとか先端度が高いとか はスター数とはあまり関係ないのですが、今回は分か りやすくTOP10ランキングからの紹介でいきます!
UniTask
UniTask (★8308) Unity用のカスタム非同期ランタイム https://github.com/Cysharp/UniTask 独自の非同期処理システムを実装 TaskはThreadPoolで動くランタイム UniTaskはゲームエンジンに特化し ゲームループ上で動くランタイム Task、のようでTaskじゃ ない独自の生態系
非同期ランタイムの差し替え await可能な型 GetAwaiter()を実装する async関数の戻り値にできる型 AsyncMethodBuilderを実装する(C# 7.0) コンパイラ生成のコードがカスタム実装のBuilderを呼ぶようにな るため、既存のTask用のBuilderを完全にバイパスできる これはたまによくやるパターン ちょっと(かなり)珍しい
非同期ランタイムの差し替え await可能な型 GetAwaiter()を実装する async関数の戻り値にできる型 AsyncMethodBuilderを実装する(C# 7.0) コンパイラ生成のコードがカスタム実装のBuilderを呼ぶようにな るため、既存のTask用のBuilderを完全にバイパスできる 全体をUniTaskで統一する、という世界観をユーザーに強いるこ とが出来れば、性能面/利便性でフレームワークに完全に特化した
カスタムの非同期ランタイムをユーザーに提供することができる このカスタマイズ性は言語的にもかなりイ ケてる部類に入る!C#すごいイイ! まぁ、現状、実用的な形でそれを実践して幅広く 利用されているランタイムはUniTaskぐらいしか ありませんが……(逆にUniTaskすごい)
MagicOnion
MagicOnion (★3869) C# CodeFirst API/Realtime RPC https://github.com/Cysharp/MagicOnion gRPC上に構築されたC#専用RPC クライアントはUnityにも対応 gRPC標準のProtocol
Buffersではなく MessagePack for C#を使用することで .proto不要のCode First RPCを実現 実はかなり歴史が古い async対応の独自型テクニックも利用して手触 り向上(Task<UnaryResult<T>> よりも UnaryResult<T>のほうが書きやすい
2016-08-23 gRPC 1.0 MagicOnion 0.1.0 2016-12-11 2019-09-20 first stable release
of gRPC for .NET googleによるC#実装も含む わずか4か月後に公開 (開発開始はgRPC 1.0リリース直後から) 更に4か月後の2017-04-26にゲームをリリース してMagicOnionがiOS/Androidで実稼働 その数十年後にようやく MicrosoftがgRPCの価値に気付く
RPC Generation 言語の違うREST Response型を別々 に書く APIクライアント を手書きする (メンテナンスも 大変で気合が必要、 何気に多い)
中間IDLを書く そこからクライア ント・レスポンス 型自動生成 (←を嫌う時によ くある構成、これ が最先端だと多く の人が勘違いして いる) サービスを普通に 書く、そこからク ライアントをコマ ンドラインツール で自動生成、リク エスト・レスポン ス型は同一言語で 共有 サービスを普通に 書く、そこからク ライアントをコン パイル時Source Generatorで静的 生成、リクエス ト・レスポンス型 は同一言語で共有 普通のREST JsonSchema/gRPC MagicOnion v6 MagicOnion v7 MagicOnionは世代が遥か未来のRPC 世間は未だこの辺
RPC Generation 言語の違うREST Response型を別々 に書く APIクライアント を手書きする (メンテナンスも 大変で気合が必要、 何気に多い)
中間IDLを書く そこからクライア ント・レスポンス 型自動生成 (←を嫌う時によ くある構成、これ が最先端だと多く の人が勘違いして いる) サービスを普通に 書く、そこからク ライアントをコマ ンドラインツール で自動生成、リク エスト・レスポン ス型は同一言語で 共有 サービスを普通に 書く、そこからク ライアントをコン パイル時Source Generatorで静的 生成、リクエス ト・レスポンス型 は同一言語で共有 MagicOnionはC# RPCの決定版となるべく、 手触りが良いことを最重要視して、現在も 最前線で開発を進めています……!
MemoryPack
MemoryPack (★3345) The fastest serializer in C# https://github.com/Cysharp/MemoryPack C#に特化した実装 +
バイナリ仕様により究極の速さを実現 MessagePack for C#(★5795)の限界を言語特化にすることで超えた 最近、.NET Foundationに参加しました! 実績(Visual StudioやSignalR, Blazorなど での採用)と相互運用性、実装の手堅さで はMessagePack for C#、エクストリーム パフォーマンスという点では MemoryPackという使い分け(?)
パフォーマンスの秘訣 Source Generator 旧来のシリアライザーの高速化はIL.Emitが定番だったが いち早くSource Generatorに特化した Span<T>, IBufferWriter<T>,ReadOnlySequence<T> モダンI/O型の標準採用 System.Runtime.CompilerServices.Unsafe
Span<T>と合わせて メモリを直接読み書きする あとは徹底的なアローケション避けや、 分岐の最小化、メソッド呼び出し回数 の削減など地道な努力……
R3
R3 (★2263) Reconstruction of Reactive Extensions https://github.com/Cysharp/R3 Rxベータ版からの超初期ユーザー(2011-)かつUnity版Rxである UniRx(2014- ★7098)の実装経験をもとに、Rxを現代的に再構築
多くのプラットフォーム対応(Unity, Godot, Avalonia, WinUI3, etc...) より高機能(FrameOperators, AwaitOperators) より分かりやすく(async/await integration) よりアクティブなメンテナンスビリティ コードの複雑さから、dotnet/reactive既に ほとんどメンテ不能になってる……
TimeProviderの全面採用 そもそもR3は存在そのものがModern C#なのだが? SystemClock.Now的な利用だけではなく、 DateTimeOffset, TimeZoneInfo, Stopwatch, Timer の抽象層になっている 標準で用意されてるFakeTimeProviderも便利
TimeProvider in R3 カスタムTimeProvider作りまくり 超大量のメソッド内利用
Use Timestamp DateTimeOffsetやStopwatchそのものは使わず、 GetTimestampで高解像度タイマー(Windowsで はQueryPerformanceCounter)から時間を取得 GetElapsedTimeでタイムスタンプ間 から経過時刻のTimeSpanを算出する
Stopwatch.GetElapsedTime from .NET 7 DateTime.UtcNow – nowは使わない! ある地点からの経過時間(TimeSpan)を取得するのに日付はいらない Stopwatch.StartNew(heap allocation)もいらない
オレオレValueStopwatchもいらない 開始地点のlongをGetTimstampで取得して保持するだけ Stopwatch.GetTimestampは昔からあるけれど、2点のtimestampから TimeSpanを取得するGetElapsedTimeは.NET 7から……! (.NET 7以前でもMicrosoft.Bcl.TimeProviderを入れて TimeProvider.System.GetElapsedTimeを使うというハックもある!) 日付の取得 is not FREE ただのlongなのでStopwatchよりむしろ取り回しがいい
ZString
ZString (★2082) Zero Allocation UTF8/UTF16 String Builder https://github.com/Cysharp/ZString 文字列周りの処理は、当時の.NETは多くの無駄があった 例えば数値型のStringBuilder.Appendでは
netstandard2.0 -> 文字列化してからバッファに書き込み netstandard2.1 -> ISpanFormattable.TryFormatで直接書き込み Formatメソッドもobject argsによるboxingが避けられない、など Utf8関連でも文字列化->UTF8エンコードといった多くの無駄が発 生していた(.NET Core 3.1辺りで改善されて、doubleはGrisu3と いったアルゴリズムで直接書き込むようになった)
Improvement Interpolated Strings C# 10.0でInterpolated Stringsが大幅改善された コンパイル時にInterpolatedStringHandler経由で処理する+適切な 一時バッファ量をコンパイル時推測することで、Zero Allocation UTF16
String Builderが実現されている UTF16関連でのZStringのアドバンテージはあまりない UTF8周りはまだ未熟のため、新世代のライブラリを作った Utf8StringInterpolation(★157) https://github.com/Cysharp/Utf8StringInterpolation Utf8StreamReader(★213) https://github.com/Cysharp/Utf8StreamReader それつまりBetter ZString...... UTF8直読み書きはパフォー マンス上最重要!が、需要 は少ないみたい……
ConsoleAppFramework
ConsoleAppFramework (★1656) Source Generator based CLI Framework https://github.com/Cysharp/ConsoleAppFramework 最新版(v5)は大幅な破壊的変更によりSource Generatorベースに完
全に作り変えられた ライブラリ自身も含めた追加の依存性ゼロ ゼロリフレクションによるNative AOT完全対応 ゼロアロケーションによる完全最適化した手書きと同じ実行速度 コールドスタートからの 実行速度も圧倒的!
マクロ的Source Generator メソッド呼び出しの中 身から生成する 多くのSource Generatorは属性ベースで動作します が、RustにはAttribute-like macrosとFunction-like macrosという分類があり、これはfunction-likeなス タイルとして着想を得た
MasterMemory
MasterMemory (★1534) Readonly In-Memory Database https://github.com/Cysharp/MasterMemory マスターデータ(実行中変動が(ほぼ)ないデータ)の特性から、 起動時にインメモリに全て読み込むことで結果的に省メモリ+高 速動作が可能になるという発想のデータベース Roslynを使ったコード生成が(当時と
しては)秘訣だったのですが、今と なっては手法が古いのでSource Generatorベースに改修中......
MessagePipe
MessagePipe (★1438) High performance in-memory messaging https://github.com/Cysharp/MessagePipe DIベースのインメモリイベント処理フレームワーク 速い!それとasync対応も嬉し い!DI対応はフレームワーク
(ASP .NET とかVContainerを使っ てる状態だとかなり嬉しい!
DI First Microsoft.Extensions.DependencyInjection よくも悪くも、今の .NET のフレームワークは全てがDIベース あらゆるフレームワークが Generic Host(Microsoft.Extensions.Hosting) の上で構築されているため
そこに全面的に乗っかるパターン と、あえてそこから逸脱する (ConsoleAppFramework v5)と いった試みもしている
Ulid
Ulid (★1334) Sortable GUID alternative https://github.com/Cysharp/Ulid 先頭48bitがTimestampなので ソート可能という性質を持つ データベースのIDとして使う場合などに、GUIDだ とランダム配置されるため性能劣化に繋がるが、
UlidだとInsert時間で固まるため性能的に優位 (Optional)単独で生成時間が分かるのも便 利といえば便利(ユーザーに露出すると問 題がある場合もあるので注意) 実用的にはGUIDのランダム性 とほとんど遜色なし
Guid.CreateVersion7(.NET 9) UUID Version 7 - RFC9562(2024-05) GUIDはUUIDのMicrosoft方言で、ほぼイコール Ulidの良くなかったところ UUIDと互換性のない文字列表現
Guidと型が違う(当然だが既存システムとの相互運用性が劣る) CreateVersion7の良いところ よくもわるくもGuidそのもの CreateVersion7の良くないところ 生成速度がGuid + Timestampで先頭上書きなのでやや劣る 性質が違うのに型が同じとい うのは厳密には良くない。が、 現実的な利便性では同じこと のほうがプラス。 つまり、私的には.NET 9以降な らGuid v7のほうがお薦めです!
public static class GuidEx { private const byte Variant10xxMask =
0xC0; private const byte Variant10xxValue = 0x80; private const ushort VersionMask = 0xF000; private const ushort Version7Value = 0x7000; public static Guid CreateVersion7() => CreateVersion7(DateTimeOffset.UtcNow); public static Guid CreateVersion7(DateTimeOffset timestamp) { // 普通にGUIDを作る Guid result = Guid.NewGuid(); // 先頭48bitをいい感じに埋める var unix_ts_ms = timestamp.ToUnixTimeMilliseconds(); // GUID layout is int _a; short _b; short _c, byte _d; Unsafe.As<Guid, int>(ref Unsafe.AsRef(ref result)) = (int)(unix_ts_ms >> 16); // _a Unsafe.Add(ref Unsafe.As<Guid, short>(ref Unsafe.AsRef(ref result)), 2) = (short)(unix_ts_ms); // _b ref var c = ref Unsafe.Add(ref Unsafe.As<Guid, short>(ref Unsafe.AsRef(ref result)), 3); c = (short)((c & ~VersionMask) | Version7Value); ref var d = ref Unsafe.Add(ref Unsafe.As<Guid, byte>(ref Unsafe.AsRef(ref result)), 8); d = (byte)((d & ~Variant10xxMask) | Variant10xxValue); return result; } public static DateTimeOffset GetTimestamp(in Guid guid) { ref var p = ref Unsafe.As<Guid, byte>(ref Unsafe.AsRef(in guid)); var lower = Unsafe.ReadUnaligned<uint>(ref p); var upper = Unsafe.ReadUnaligned<ushort>(ref Unsafe.Add(ref p, 4)); var time = (long)upper + (((long)lower) << 16); return DateTimeOffset.FromUnixTimeMilliseconds(time); } netstandard2.0向けにこういうの 作っちゃって、Guid v7に統一する のを選ぶかも Timestampの取得メソッドは標準 にはないので用意してもいいかも
Endianに注意 GUIDの内部表現はLittleEndian ToByteArray(bool bigEndian)やTryWriteBytes(bool bigEndian)で 出力時のエンディアンを決定するという方式になっている 無指定の場合はリトルエンディアンになる v7はデータベースに格納する場合はBig推奨 タイムスタンプ部でSortするからバイナリ表現としてBigが適切 C#側のデータベースドライバーに注意
GUIDがBigで書き込む設定になっていないと逆に性能劣化 mysqlconnector-netは接続文字列でGuidFormat=Binary16にする (デフォルトがそうなのでmysqlconnector-netは基本的に大丈夫)
Endianに注意 GUIDの内部表現はLittleEndian ToByteArray(bool bigEndian)やTryWriteBytes(bool bigEndian)で 出力時のエンディアンを決定するという方式になっている 無指定の場合はリトルエンディアンになる v7はデータベースに格納する場合はBig推奨 タイムスタンプ部でSortするからバイナリ表現としてBigが適切 C#側のデータベースドライバーに注意
GUIDがBigで書き込む設定になっていないと逆に性能劣化 mysqlconnector-netは接続文字列でGuidFormat=Binary16にする (デフォルトがそうなのでmysqlconnector-netは基本的に大丈夫) PostgreSQLも、npgsqlの標準のconverterがそうなってるので大丈夫。 Microsoft SQL Serverは悲しいことにダメで議論進行中。 https://github.com/dotnet/SqlClient/discussions/2999 新しいドライバのほう(Microsoft.Data.SqlClient)でダメなので、古いドライ バ(System.Data.SqlClient)はなおダメでしょう。
ZLogger
ZLogger (★1286) Zero Allocation Text/Structured Logger https://github.com/Cysharp/ZLogger 速い&デフォルト設定が速い!
Extreme Interpolated String カスタムInteprolatedStringHandlerにより、 String(UTF16)を介さずにUTF8のロギング用バッ ファーに直接書き込みすることでゼロアロケー ション&超高速化
Conclusion
C#の可能性を切り開いていく 言語は進化する、ではライブラリは? 可能性は両輪で切り開いていかなければならない 言語が進化するなら、ライブラリも必然的に進化する 時には破壊的変更も厭わない……! 使いやすさとパフォーマンスを引き出す そのための新しいC#であり、新しい.NET C#をあらゆる言語においてベストな環境にすることに Cysharpは貢献しています……!
None