2016-06-18 52 views
10

'u çağırırken CLR'den benden daha hızlı nasıl şaşırtıcı bir şey bulduğumda (bana) farklı bir zaman damgası oluşturma yöntemlerini test ettim. Windows'un GetSystemTimeAsFileTime kullanarak P çağrılmasıWindows API

/Invoke dahili aynı GetSystemTimeAsFileTime için CLR'nin sarıcı kullanır DateTime.UtcNow arayarak yaklaşık 3 kat daha yavaştır.

Bu nasıl olabilir?

İşte DateTime.UtcNow's implementation var:

public static DateTime UtcNow { 
    get { 
     long ticks = 0; 
     ticks = GetSystemTimeAsFileTime(); 
     return new DateTime(((UInt64)(ticks + FileTimeOffset)) | KindUtc); 
    } 
} 

[MethodImplAttribute(MethodImplOptions.InternalCall)] // Implemented by the CLR 
internal static extern long GetSystemTimeAsFileTime(); 

çekirdek CLR en wrapper for GetSystemTimeAsFileTime:

FCIMPL0(INT64, SystemNative::__GetSystemTimeAsFileTime) 
{ 
    FCALL_CONTRACT; 

    INT64 timestamp; 

    ::GetSystemTimeAsFileTime((FILETIME*)&timestamp); 

#if BIGENDIAN 
    timestamp = (INT64)(((UINT64)timestamp >> 32) | ((UINT64)timestamp << 32)); 
#endif 

    return timestamp; 
} 
FCIMPLEND; 

Testim kodu BenchmarkDotNet kullanan:

public class Program 
{ 
    static void Main() => BenchmarkRunner.Run<Program>(); 

    [Benchmark] 
    public DateTime UtcNow() => DateTime.UtcNow; 

    [Benchmark] 
    public long GetSystemTimeAsFileTime() 
    { 
     long fileTime; 
     GetSystemTimeAsFileTime(out fileTime); 
     return fileTime; 
    } 

    [DllImport("kernel32.dll")] 
    public static extern void GetSystemTimeAsFileTime(out long systemTimeAsFileTime); 
} 

Ve sonuç:

    Method |  Median | StdDev | 
------------------------ |----------- |---------- | 
GetSystemTimeAsFileTime | 14.9161 ns | 1.0890 ns | 
        UtcNow | 4.9967 ns | 0.2788 ns | 
+2

CLR doğrudan arayabilirsiniz: Ayrıca ben Voigt olarak unsafe kullanılarak

yarıya tekrar getiriyor önerdi. Pinvoke, marshalling katmanından geçer. –

+0

@DavidHeffernan, parametrelerin sıralı olmasına gerek kalmasa bile mi? – i3arnon

+1

@ i3arnon: Bir şeyin bunları kanıtlamak için analiz etmesi gerekiyor. –

cevap

4

CLR, sonucu almak için yerel (otomatik, yığın) bir değişkene neredeyse bir işaretçi gönderir. Yığın sıkıştırılmıyor ya da yeniden yerleştirilmiyor, bu nedenle belleğe, vb. Iğnelemeye gerek yok ve yerel bir derleyici kullanırken, bu tür şeyler zaten desteklenmiyor, dolayısıyla bunları hesaba katmaya gerek yok.

C# içinde, p/invoke bildirimi, çöp toplanan yığında yaşayan yönetilen bir sınıf örneğinin bir üyesini geçirmeyle uyumludur. P/invoke, bu örneği sabitlemeli ya da OS işlevi yazmadan önce/çıkış tamponunun hareket etmesini riske atmalıdır. Yığında saklanan bir değişkeni geçseniz de, p/invoke hala test etmeli ve işaretçinin toplanan kodun etrafında toplanmadan önce toplanan çöpün içinde olup olmadığını görmeli, böylece aynı durum için bile sıfır olmayan yük var.

O artık tamamen kod sorumluluğu ayarlar out parametre, s/artık aliasing ve yığın sıkıştırma ile başa çıkmak için vardır çağırmak, o oluyor o ortadan kaldırarak

[DllImport("kernel32.dll")] 
public unsafe static extern void GetSystemTimeAsFileTime(long* pSystemTimeAsFileTime); 

kullanarak daha iyi sonuçlar alabilir mümkündür işaretçiyi yukarı.

4

Yönetilen kod yönetilmeyen kod çağırdığında, çağrı kodunun UnmanagedCode izninin bunu yapmasına izin verdiğinden emin olarak bir yığın yürüyüşü vardır.

Bu yığınlama, çalışma zamanında yapılır ve performans açısından önemli maliyetlere sahiptir.

O SuppressUnmanagedCodeSecurity attribute kullanarak (hala bir JIT derleme zamanı tane var) çalışma zamanı işaretini kaldırın mümkündür:

:

[SuppressUnmanagedCodeSecurity] 
[DllImport("kernel32.dll")] 
public static extern void GetSystemTimeAsFileTime(out long systemTimeAsFileTime); 

Bu CLR en doğru yaklaşık yarım benim uygulanmasını getiriyor

    Method | Median | StdDev | 
------------------------ |---------- |---------- | 
GetSystemTimeAsFileTime | 9.0569 ns | 0.7950 ns | 
        UtcNow | 5.0191 ns | 0.2682 ns | 

Bunu yapmanın son derece riskli güvenlik açısından olabileceğini aklınızdan çıkarmayın.

    Method | Median | StdDev | 
------------------------ |---------- |---------- | 
GetSystemTimeAsFileTime | 6.9114 ns | 0.5432 ns | 
        UtcNow | 5.0226 ns | 0.0906 ns |