c# - 调用Windows API时CLR怎么比我快

标签 c# .net performance pinvoke clr

当我发现(对我来说)令人惊讶的事情时,我测试了生成时间戳的不同方法。

使用 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*)&timestamp);

#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/

相关文章:

c# - 将文本复制到剪贴板

c# - 覆盖基类的内部只读属性

c# - 在 MVVM 中,为什么人们要创建 ViewModel 文件夹?

c# - 如何使用 C# 和 .NET 框架创建 Windows 7 通知区域弹出窗口?

java - 如何使用 currentTimeMillis() 方法获取小部分执行时间

ruby-on-rails - 如何对我的 Rails 应用程序的启动进行基准测试?

java - 为什么这个 Clojure 程序这么慢?如何让它跑得快?

c# - 为什么这个附加属性没有更新?

c# - 将(许多)JSON 不同对象解析为 C# 类。强类型更好吗?

c# - 如何通过网络复制经常使用的文件?