投稿

[C#]stackallocでstructをスタックに確保してパフォーマンスを上げよう戦略

イメージ
略してstackalloc戦略です。

前回の記事でin引数戦略(最終的にはref戦略な感じになりましたけど)を提示してみましたが、in引数戦略では使える幅が狭いです。

なぜかというと、配列が使えないから。

.NETでは配列はヒープに確保されるので、原理上、配列の値をローカル変数に持ってくると値のコピーが発生してしまいます。
それではin引数戦略はほとんどの場合で使い物になりません。

そこでstackalloc戦略です。
C# 7.2で安全なstackallocであればunsafeコンテキスト以外で使用できるようになりました。安全というのはSpan<T>と併用している場合です。
(Span<T>は.NET Core 2.1以外ではSystem.Memoryパッケージが必要とされます)

stackallocを用いてスタック上に配列のようなもの(Span<T>)を確保することで、in引数戦略の効力を高めようという感じです。

肝心のベンチマーク 今回もBenchmarkDotNetを使用しています。 今回は.NET Core 2.0と2.1のパフォーマンス差も確認したいので、csprojは以下のような感じの記述を追加しています。 <TargetFrameworks>netcoreapp2.0;netcoreapp2.1;net47</TargetFrameworks> <PlatformTarget>AnyCPU</PlatformTarget> また、CoreJobもそのままでは使えないので、新しく属性を定義してやります。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly)] public class Core20JobAttribute : Attribute, IConfigSource { public Core20JobAttribute() { Config = ManualConfig.CreateEmpty().With(Job.Core.With(CsProjCoreToolchain.NetCo…

[C#]Big Size Structが値コピーでつらいならin引数で値コピーしなければいいじゃない!! < それ本当?

イメージ
C#で高速なプログラムにする際のお供な構造体ですが、構造体を使わないほうがいい場面・条件もあります。
その一つに"サイズが16バイト未満であること"があります。この16バイトという数字の根拠は明確にはわかりませんが、構造体の特性上よく値コピーが発生するのでサイズが大きければ大きいほど値コピーのコストが高まるということからある程度のサイズまでのものがいいというのは想像つくと思います。

そしてこの値コピーが発生する個所としてよくあるのがメソッド呼び出しの引数に構造体を渡したときなのですが、C# 7.2で追加されたin引数/ref readonlyを使うと読み取り専用参照として渡せるので値コピーを抑制することができるようになりました。

そこで「値コピーがつらくてBig Size Structが使えないなら値コピーしなければいいじゃん」(以下in引数戦略)という疑問が生まれます。

実際に測ってみた 値コピーありのメソッド呼び出しとin引数で参照を渡すメソッド呼び出しの相対的な速度比を計測してみることにします。

利用するのはベンチマークのお供なBenchmarkDotNetです。計測実行環境としては.NET Frameworkと最近パフォーマンスが向上した.NET Coreです。(もちろんのことながらホストマシンはWindowsです)
計測対象はこのような感じの構造体です readonly struct Size16 { public readonly int A, B, C, D; } readonly struct Size32 { public readonly Size16 A, B; } readonly struct Size64 { public readonly Size32 A, B; }
Size32からフィールド宣言がめんどくさくて定義済みの構造体を利用していますが、計測したいのはサイズごとの相対比なので問題ないでしょう。

また、計測メソッドは下のような感じです。
[Benchmark] public int Size32NormalCall() { Size32 v = default; return size32(v); } [Benchmark] public int Size32I…

BenchmarkDotNetを.NET Frameworkと.NET Coreで走らせる方法

C#でベンチマークを取る際に非常に楽に実施できるツールのBenchmarkDotNetで.NET Frameworkと.NET Core両方でベンチマークを取る方法がわかったのでメモ取っておきます。

内容はBenchmarkDotNetのFAQに書いてあります。
(ちゃんとFAQ読め案件)

ベンチマーク対象に.NET Frameworkと.NET CoreのJobを付けるだけでは、ベンチマーク実施時にどちらかのJobで失敗してしまいます。

[ClrJob, CoreJob] public class Bench{ }
指定方法は上のような感じです。

原因は簡単でTargetFrameworkが普通にプロジェクトを作成したらどちらか片方だけになってしまうからです。

そのため、csprojを弄って両方のTargetFramework指定をする必要があります。
自分の場合は元のprojectが.NET Framework向けでcsprojがややこしかったので、.NET Core projectを作成しなおしました。

csprojを開くと
<TargetFramework>netcoreapp2.0</TargetFramework>
のような箇所があるので以下のように書き換えましょう
<TargetFrameworks>netcoreapp2.1;net47</TargetFrameworks> <PlatformTarget>AnyCPU</PlatformTarget>
最新の.NET Core 2.1でパフォーマンスが向上しているのでベンチマークを取る際は通常は2.1指定でいいと思います。
.NET Frameworkはバージョン間の違いがわからなくなってきてる節がありますが、とりあえず新しいものでベンチマークを取りましょう。

csprojを編集したら、ベンチマーク対象にJobを付けてやることで.NET Frameworkと.NET Coreでベンチマークが取れるはずです。
(そもそも.NET Frameworkな時点で実行OSはWindowsであることが求められますけどね)

.NET Standardで埋め込みリソースを読み込む方法

イメージ
Xamarin.Formsを使う場合、共通リソースはPCLや.NET Standardで管理したいですよね。
それを実現する一つの手段としてリソースを埋め込むという手法があります。

脳死でネットを検索するとPCLでの場合は出てきますが、最近のトレンドは.NET Standardなので、そちらの方法を紹介します。

当方の環境はVisual Studioですが、Visual Studio for MacやRiderでもUIは違っても同じようにできるはずです。

ファイルを.NET Standardプロジェクトに追加したらプロパティウィンドウで該当ファイルを見てください(プロパティウィンドウがなければ左上のメニューの表示=>プロパティウィンドウで開けます)
ビルドアクションを埋め込みリソースにしたら、ファイルの準備は完了です。

// TimeTableRepositoryクラスのメソッド内 // TimeTableRepositoryはファイルを埋め込んだプロジェクト内に存在する // Assemblyを取るのにtypeofでてきとーなクラス指定してやるのが簡単 // PCLではType.GetTypeInfo().Assembly でらしいが.NET Standardでは Type.Assembly で取得する Assembly assenbly = typeof(TimeTableRepository).Assembly; // 引数は 名前空間 + ファイル名 using (var stream = assenbly.GetManifestResourceStream("Tbus.App.NETStandard.Resources.kansai_takatuki.json") ?? throw new Exception("not found file")) using (var streamReader = new StreamReader(stream)) { string text = await streamReader.ReadToEndAsync(); }
ファイルを取得するにはこんな感じでやってみるといいと思います(こなみ)。 ファイルのパスがどうなってるのかいまいち推測しにくい!や、ほんとに…

[C#]Generic indexersが欲しい

突然ですが、ふとこんなコードを
T Get(string key) { ... }
こんな風に
T this[string key] { get { ... } }
書きたくなったことありませんか?
私はあります。generic methodをgeneric indexerにできるだろうと、試してみるもコンパイルエラー。

比較的優秀なジェネリクスを持っているC#ならできるだろうと思ったりしますができません。

いちおうRoslynのほうにIssueは上がってたりします。

が、どうもCLRに変更を加える時に盛り込みたいような内容で、csharplangのほうにIssueとして、リストアップされてます。

interfaceのデフォルトメソッド対応の時に盛り込まれればラッキーみたいな内容ですが、盛り込まれるかどうかもわからないので、当分はgeneric methodで我慢ですね。。。

[C#]コンストラクタで参照渡しできた話と出力変数宣言できるようになる話

よく++C++; // 未確認飛行 Cにはお世話になっていますが、その中のC#7.3新機能説明(式中での変数宣言をできる箇所の拡大)について、お恥ずかしながらよくわからないC#コードがあり、新たな発見をしたので、その報告をします。

式中での変数宣言(使える場所の拡充)
問題のコードを抜粋すると以下のようなものです
using System; class Derived : base { public Derived(int a) : base(out var x) { // base の場合でも同様。 Console.WriteLine(x); } }
このコンストラクタでのbaseの箇所""out var x""はなんなのか、当初はちっとも検討が付きませんでしたが、同僚の指摘によってどのようになってるかわかりました。

まず、上記のコードは基底クラスが省かれてるので同じような記述をすると以下のようになります。
class Base { // コンストラクタ public Base(out int a) { a = 10; } } class Sub : Base { // コンストラクタ public Sub() : base(out int a) { Console.WriteLine(a); // 10 } }
ここでBaseクラスのコンストラクタではint型の参照渡し(正確には出力引数)になっています。
気づきポイント①:コンストラクタでも参照渡しできた

そして、C#7.3では式中での変数宣言のできる箇所が拡大されたため、Subクラスのコンストラクタのbaseでは出力変数宣言になっています。
気づきポイント②:コンストラクタで出力変数宣言ができるようになった

ちなみにですが、Baseクラスのコンストラクタでは参照渡しの引数なので型の明示が必要ですが、Subクラスのコンストラクタでは出力変数宣言なのでvarによる型推論も可能です。




C#歴6年目ぐらいになりましたが、まだまだ知らない機能があってC#は勉強してて楽しいです。

Kotlinを学ぶにはC#を勉強したほうがいい(SequenceとLINQ)

タイトル半分ぐらい釣りです(半分大真面目)

最初に断っておきますが、Kotlinを勉強するのに必ずしもC#を勉強したほうがいいわけではありません、もちろんVB.NETでも概念は学べますし代替手段はいくらでもあると思います。

そこで、なぜC#を勉強したほうがというと単純に知見の数やKotlinはC#を意識しているとかいろいろありますが、自分がC#を推しているからです。

Sequence ところで、Kotlin使いの皆さんは本題のSequenceについてご存知でしょうか? 知らない方も多いかと思います。自分も1年ぐらい知りませんでした。
簡単に説明すると遅延実行するリスト操作関数です(Sequence自体はインターフェースです)。
遅延実行とはなんぞやって方はそのまま「C# LINQ 遅延評価」などでググってどうぞ。
そこで、「なぜSequenceがあるのか?Sequenceを使うと何が美味しいの?」というと単純に説明すると省メモリにできます(ツッコミどころのある説明だけどマサカリは飛ばさないで)。 このあたりの利点は「C# LINQ」などでググったらちらほらと見れるはずです。
「じゃあ、それなりに利点あるし使いたい!けど、わからない、どうやって勉強したらいいの?」 ってなりますよね? そこでC#のLINQ(正確にはLINQ to Objects)ですよ。 LINQ LINQというと.NETが提供する機能ですが、ほとんどの場合C#で用いているものを指していると思います。 そんなLINQですがWikipediaによるとVisual Studio 2008から対応しているらしいので、単純計算で10年近くの歴史があることになります。 Kotlinより歴史ありますよね?そういうわけで、今のところ知見的には同じような概念なので、KotlinのSequenceを学ぶならC#のLINQを学べばいいということになります。
ただし、KotlinのSequenceを学ぶためという用途ならば、学ぶのはLINQ to Objectsだけで十分かと思います。LINQは他にもLINQ to SQLなどがあります。 Reactive Extensions(Rx) LINQの話をするとこちらにも飛んできちゃいます、そうReactive Extensionsです。 他言語界隈の人ならRxのほうが身近な単…