在F#中,我們有設計時類型安全幾個非常不錯的解決方案:(!和隱式轉換下手)類型別名和單例結構工會:C#標記結構性能
什麼是C#的替代方案?
我從來沒有見過的標記結構的實際使用(包含單個元素),但看起來如果我們增加明確類型轉換,則我們可以得到非常相似,鍵入F#別名設計時行爲。那就是 - IDE會抱怨類型不匹配,並且必須顯式地賦值。
下面是一些POC代碼:
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的事情是太好了!但我會問什麼是性能影響和其他缺點,並避免「只是測量」評論剛剛測量它,並最初停止寫這個問題......但結果出來反直覺:
[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方法,並且有更多的行 - 所以從IL的角度來看,它的工作更多。但除此之外,代碼是相同的。如果結果是可重現的,可能會有一些JIT特性。 –
結果是「真」的64位位不同(身高:32位關閉),但一般來說,我不會打擾性能 - 一個'struct'被包裝的單個原始構件確實應該與使用該成員。 –