は、我々が設計時の型の安全性のためのいくつかの非常に素晴らしいソリューション持ってパフォーマンス:(!で起動すると無暗黙の変換)型の別名とシングルケース構造体組合:C#のマーカー構造物は、F#では
C#の代替手段は何ですか?
私はマーカー構造物の実用的な使い方(単一の要素を含む)を見たことがないが、それは我々が明示的型変換を追加した場合、我々は、F#でのエイリアスを入力することは非常に似たデザイン時の動作を得ることができるようになります。つまり、IDEは型の不一致について不満を持ち、明示的に値をキャストする必要があります。
public struct Offset {
private readonly long _value;
private Offset(long value) {
_value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Offset(long value) {
return new Offset(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator long(Offset offset) {
return offset._value;
}
}
public interface IIndex<T> {
Offset OffsetOf(T value);
T AtOffset(Offset offset);
}
public class SmapleUsage
{
public void Test(IIndex<long> idx)
{
// without explicit cast we have nice red squiggles
var valueAt = idx.AtOffset((Offset)123);
long offset = (long)idx.OffsetOf(42L);
}
}
ので、IDEのものがいいです:
以下は、いくつかのPOCコードです!しかし、私はに何がパフォーマンスの影響と他の弱点であるのかと尋ねるつもりでした。そして、「ちょうどそれを測定する」というコメントはちょうどそれを測定し、この質問を最初に書くのを止めました...しかし、結果は直感に反していました:
[Test]
public void OffsetTests() {
var array = Enumerable.Range(0, 1024).ToArray();
var sw = new Stopwatch();
for (int rounds = 0; rounds < 10; rounds++) {
sw.Restart();
long sum = 0;
for (int rp = 0; rp < 1000000; rp++) {
for (int i = 0; i < array.Length; i++) {
sum += GetAtIndex(array, i);
}
}
sw.Stop();
if (sum < 0) throw new Exception(); // use sum after loop
Console.WriteLine($"Index: {sw.ElapsedMilliseconds}");
sw.Restart();
sum = 0;
for (int rp = 0; rp < 1000000; rp++) {
for (int i = 0; i < array.Length; i++) {
sum += GetAtOffset(array, (Offset)i);
}
}
if (sum < 0) throw new Exception(); // use sum after loop
sw.Stop();
Console.WriteLine($"Offset: {sw.ElapsedMilliseconds}");
sw.Restart();
sum = 0;
for (int rp = 0; rp < 1000000; rp++) {
for (int i = 0; i < array.Length; i++) {
sum += array[i];
}
}
if (sum < 0) throw new Exception(); // use sum after loop
sw.Stop();
Console.WriteLine($"Direct: {sw.ElapsedMilliseconds}");
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetAtIndex(int[] array, long index) {
return array[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetAtOffset(int[] array, Offset offset) {
return array[(long)offset];
}
驚くべきことに、[email protected] x64の上/ Offset
でケースをリリースラウンドすべてのテストに目に見えて速くなる - 典型的な値は次のとおりです。
Int64: 1046
Offset: 932
Direct: 730
私はちょうどint64
を使用する場合と比較して同等か、より遅い結果を期待します。だからここには何が起こっているのですか?同じ違いを再現するか、いくつかの欠点を見つけることができますか?私は別のものを測定する場合ですか?
あなたは生成されたILを見ましたか? – thumbmunkeys
@thumbmunkeys、はい、明らかに 'Offset'ではop_explicitメソッドを呼び出し、より多くの行を持っています。しかし、それ以外のコードは同じです。結果が再現可能であれば、いくつかのJIT特異性があると思われる。 –
結果は "真の" 64ビット(32ビットオフを推奨)とは少し異なりますが、一般的にパフォーマンスは気にしません。単一のプリミティブメンバーをラッピングしている 'struct'は実際にはメンバー。 –