c# – 调用Windows API时CLR的速度比我快

前端之家收集整理的这篇文章主要介绍了c# – 调用Windows API时CLR的速度比我快前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
当我发现一些令人惊讶的事情(对我来说),我测试了不同的生成时间戳的方式.

使用P / Invoke调用Windows的GetSystemTimeAsFileTime比调用DateTime.UtcNow慢约3倍,内部使用CLR的包装器来获取相同的GetSystemTimeAsFileTime.

怎么可能?

这是DateTime.UtcNow‘s implementation

  1. public static DateTime UtcNow {
  2. get {
  3. long ticks = 0;
  4. ticks = GetSystemTimeAsFileTime();
  5. return new DateTime( ((UInt64)(ticks + FileTimeOffset)) | KindUtc);
  6. }
  7. }
  8.  
  9. [MethodImplAttribute(MethodImplOptions.InternalCall)] // Implemented by the CLR
  10. internal static extern long GetSystemTimeAsFileTime();

核心CLR的wrapper for GetSystemTimeAsFileTime

  1. FCIMPL0(INT64,SystemNative::__GetSystemTimeAsFileTime)
  2. {
  3. FCALL_CONTRACT;
  4.  
  5. INT64 timestamp;
  6.  
  7. ::GetSystemTimeAsFileTime((FILETIME*)&timestamp);
  8.  
  9. #if BIGENDIAN
  10. timestamp = (INT64)(((UINT64)timestamp >> 32) | ((UINT64)timestamp << 32));
  11. #endif
  12.  
  13. return timestamp;
  14. }
  15. FCIMPLEND;

我的测试代码利用BenchmarkDotNet

  1. public class Program
  2. {
  3. static void Main() => BenchmarkRunner.Run<Program>();
  4.  
  5. [Benchmark]
  6. public DateTime UtcNow() => DateTime.UtcNow;
  7.  
  8. [Benchmark]
  9. public long GetSystemTimeAsFileTime()
  10. {
  11. long fileTime;
  12. GetSystemTimeAsFileTime(out fileTime);
  13. return fileTime;
  14. }
  15.  
  16. [DllImport("kernel32.dll")]
  17. public static extern void GetSystemTimeAsFileTime(out long systemTimeAsFileTime);
  18. }

结果:

  1. Method | Median | StdDev |
  2. ------------------------ |----------- |---------- |
  3. GetSystemTimeAsFileTime | 14.9161 ns | 1.0890 ns |
  4. UtcNow | 4.9967 ns | 0.2788 ns |

解决方法

CLR几乎肯定会传递一个指向本地(自动,堆栈)变量的指针来接收结果.堆栈没有被压缩或重新定位,所以不需要引导内存等,当使用本地编译器时,这样的东西不支持,所以没有开销来解释它们.

在C#中,p / invoke声明与传递生活在垃圾回收堆中的受管理类实例的成员兼容. P / invoke必须固定该实例,否则在OS功能写入之前/之前有输出缓冲区的风险.即使您传递存储在堆栈上的变量,p / invoke仍然必须测试,看看指针是否进入垃圾回收堆,然后才能分支到固定代码,因此即使在相同的情况下也会有非零开销.

你可以使用更好的结果

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

通过消除out参数,p / invoke不再需要处理别名和堆压缩,现在完全是您设置指针的代码的责任.

猜你在找的C#相关文章