当我发现(对我来说)令人惊讶的事情时,我测试了生成时间戳的不同方法。
使用 P/Invoke 调用 Windows 的 GetSystemTimeAsFileTime
比调用 DateTime.UtcNow
慢大约 3 倍,后者在内部使用 CLR 的包装器来实现相同的 GetSystemTimeAsFileTime
.
这怎么可能?
这是 DateTime.UtcNow
's implementation :
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();
核心 CLR wrapper for GetSystemTimeAsFileTime
:
FCIMPL0(INT64, SystemNative::__GetSystemTimeAsFileTime)
{
FCALL_CONTRACT;
INT64 timestamp;
::GetSystemTimeAsFileTime((FILETIME*)×tamp);
#if BIGENDIAN
timestamp = (INT64)(((UINT64)timestamp >> 32) | ((UINT64)timestamp << 32));
#endif
return timestamp;
}
FCIMPLEND;
我的测试代码利用 BenchmarkDotNet :
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);
}
结果:
Method | Median | StdDev |
------------------------ |----------- |---------- |
GetSystemTimeAsFileTime | 14.9161 ns | 1.0890 ns |
UtcNow | 4.9967 ns | 0.2788 ns |
最佳答案
当托管代码调用非托管代码时,会进行堆栈遍历,确保调用代码具有启用该操作的 UnmanagedCode 权限。
堆栈遍历是在运行时完成的,并且在性能上有很大的成本。
可以使用 SuppressUnmanagedCodeSecurity
attribute 删除运行时检查(仍有 JIT 编译时检查) :
[SuppressUnmanagedCodeSecurity]
[DllImport("kernel32.dll")]
public static extern void GetSystemTimeAsFileTime(out long systemTimeAsFileTime);
这使我的实现接近 CLR 的一半:
Method | Median | StdDev |
------------------------ |---------- |---------- |
GetSystemTimeAsFileTime | 9.0569 ns | 0.7950 ns |
UtcNow | 5.0191 ns | 0.2682 ns |
请记住,这样做在安全方面可能极具风险。
同样按照 Ben Voigt 的建议使用 unsafe
将它再次带到一半:
Method | Median | StdDev |
------------------------ |---------- |---------- |
GetSystemTimeAsFileTime | 6.9114 ns | 0.5432 ns |
UtcNow | 5.0226 ns | 0.0906 ns |
关于c# - 调用Windows API时CLR怎么比我快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37898579/