V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
bthulu
V2EX  ›  .NET

.net core 的时间日期类为啥这么慢, 跟 Java 至少几十倍的差距

  •  
  •   bthulu · 2022-09-03 11:51:03 +08:00 · 5737 次点击
    这是一个创建于 810 天前的主题,其中的信息可能已经有所发展或是发生改变。

    测试环境: win10 ltsc 2021, .net6, java8
    基于.net 没啥好用的日志组件, 就自己写了.
    因为每条日志都要记录一个时间, 结果发现 C#的 DateTime.Now 是真的慢, 跟 java 的 new Date()对比了下,至少是几十倍的的性能差距.
    调用 1 亿次 DateTime.Now 要好几秒, 而调用 1 亿次 new Date()只要几百毫秒.
    而且我还是在 windows 上测试的, java 日期在 windows 上本身就比 linux 慢一两个数量级, 这要是放到 linux 下测试, .net core 日期操作岂不是要比 java 慢上几百倍?
    java 还可以通过 System.currentMillis 直接获取当前时间戳, 省掉一大堆无必要的操作. .net core 我找了很久, 貌似没有这个东西.

    第 1 条附言  ·  2022-09-06 16:53:07 +08:00

    就一个很简单很简单的测试, 懒得放代码, 果然被攻击了, 那就给一下代码吧.
    另外楼下提到的DateTimeOffset.Now.ToUnixTimeMilliseconds(), 请注意一下, .net日期类里的Now和UtcNow不是静态字段, 而是C#特有的属性, 类似java里的方法, 看似没带小括号, 实际是对get方法的调用. DateTime.Now实际是要new一个DateTime的.
    我个人觉得对当前时间戳的调用, 应该会是一个相当频繁的操作, 不仅仅是日志里用的多, 所以对于时间戳性能的要求, 是再怎么过份都不为过的. 有时就是想拿一下时间戳, 甚至都不需要进一步格式化, 这个时候java里能通过System.currentTimeMillis()直接拿到, 而.net里对应的方法是DateTime类里面的private static unsafe readonly delegate* unmanaged[SuppressGCTransition]<ulong*, void> s_pfnGetSystemTimeAsFileTime, 外部程序是没法调用的, 只能捏着鼻子先new一个DateTime再去拿时间
    还有说.net日志库好用的很, 我自己写是多此一举. 这个绝大多数情况下都是对的. 但是新发的nlog5.0改了多少默认设置(https://nlog-project.org/2021/08/25/nlog-5-0-preview1-ready.html), 真有那么好用, 还做这么大的改动干嘛? serilog这个我使用过程中, 丢过一次日志, 而且屁大点功能几十行代码也要分个模块, 项目build后serilog相关的dll占了所有dll的一半还有多, 大有向node_modules看齐的趋势


    --------------.net6.0.8 测试

    using System.Diagnostics;
    
    // warm up
    for (var i = 0; i < 100_000_000; i++)
    {
        var now = DateTime.Now;
    }
    
    // start test
    var watch = Stopwatch.StartNew();
    for (var i = 0; i < 100_000_000; i++)
    {
        var now = DateTime.Now;
    }
    
    Console.WriteLine(watch.ElapsedMilliseconds);
    

    耗时 3818 毫秒


    --------------ms jdk11.0.16.1 (ms1.8 的 jdk 官网找不到了)

    import java.util.Date;
    
    public class Main {
        public static void main(String[] args) {
            // warm up
            for (var i = 0; i < 100_000_000; i++)
            {
                var now = new Date();
            }
        
            // start test
            long start = System.currentTimeMillis();
            for (var i = 0; i < 100_000_000; i++)
            {
                var now = new Date();
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }
    
    

    耗时 364 毫秒

    43 条回复    2022-09-07 17:17:20 +08:00
    u823tg
        1
    u823tg  
       2022-09-03 12:27:23 +08:00
    上代码啊,可以帮忙跑下。
    userforg2021
        2
    userforg2021  
       2022-09-03 12:40:34 +08:00
    大佬不先点评一下 Serilog 、NLog 、Microsoft.Extensions.Logging 、log4net 这些怎么不好用?
    thinkershare
        3
    thinkershare  
       2022-09-03 12:42:49 +08:00   ❤️ 4
    不知道你在说啥, 你这样提问, 不提供代码, 没有任何意义. .NET 日志库一大堆, 我完全不相信你自己写的日志组件能达到第三方日志组件的水平, 因为你一看对.NET 常规 API 都不熟练.
    chouchoui
        4
    chouchoui  
       2022-09-03 12:58:11 +08:00 via iPhone   ❤️ 12
    基于.net 没啥好用的日志组件, 就自己写了》
    谢谢 Java boy 今天创造的笑话。
    Leviathann
        5
    Leviathann  
       2022-09-03 13:01:59 +08:00 via iPhone
    为什么要用一个被官方标记 deprecated 的类
    我说的是 date
    thinkershare
        6
    thinkershare  
       2022-09-03 13:21:04 +08:00   ❤️ 1
    https://imgur.com/QUQOi6q.jpg ,Java 提供的 Date 类型早就被废弃, 和 DateTime 提供的 API 都没法公平比较. 根本就不应该被使用. .NET 提供的 DateTime 其实也应该被废弃, 使用 DateTimeOffset 替代.
    Bingchunmoli
        7
    Bingchunmoli  
       2022-09-03 13:29:47 +08:00 via Android
    @thinkershare 实际是项目依旧 1.8 ,有些序列化支持 D'ateTime 还需要额外操作,date 实际代码用的还是更多。 还是早点更替把,框架支持跟上
    nightwitch
        8
    nightwitch  
       2022-09-03 14:01:19 +08:00
    @thinkershare 字体不错,求分享
    Maboroshii
        9
    Maboroshii  
       2022-09-03 14:07:15 +08:00 via Android
    .net 性能确实不咋地… 但是最近也需要用.net 开发。
    搭车请教一下.net6 怎么用 github 上别人写的库?我拉下来发现.net 版本不一致都不能跑… 是一个一个文件复制到我自己的项目里面用吗
    a33291
        10
    a33291  
       2022-09-03 14:08:45 +08:00   ❤️ 4
    系统 win11 22h2 22622.590
    cpu i5-9600K
    内存 32G

    以下代码均不开优化,否则可能会被优化掉

    场景 1 格式化为字符串
    C# netcore 6.0.400
    ```
    using System;
    using System.Diagnostics;

    var sw = Stopwatch.StartNew();
    for (var i = 0; i < 1_000_000; i++)
    {
    var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    //var now = DateTime.Now;
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
    ```
    Java 1.8.0.333
    ```
    package com.company;

    import java.util.Date;
    import java.text.SimpleDateFormat;

    class Playground {
    public static void main(String[] args) {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    long stime = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
    Date now = new Date();
    String nowString = format.format(now);
    }
    long etime = System.currentTimeMillis();
    System.out.printf("%d ", (etime - stime));
    }
    }
    ```
    以上代码分别执行 5 次
    C# 243 232 237 233 236
    Java 617 629 619 632 616

    场景 1 只读取当前时间,不做格式化
    C# netcore 6.0.400
    ```
    using System;
    using System.Diagnostics;

    var sw = Stopwatch.StartNew();
    for (var i = 0; i < 1_000_000; i++)
    {
    //var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    var now = DateTime.Now;
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
    ```
    Java 1.8.0.333
    ```
    package com.company;

    import java.util.Date;
    import java.text.SimpleDateFormat;

    class Playground {
    public static void main(String[] args) {
    //SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    long stime = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
    Date now = new Date();
    //String nowString = format.format(now);
    }
    long etime = System.currentTimeMillis();
    System.out.printf("%d ", (etime - stime));
    }
    }
    ```
    以上代码分别执行 5 次
    C# 39 38 39 39 39
    Java 9 10 10 9 9

    结论
    1. 均执行时间字符串格式化时,C#比 Java 快
    2. 仅执行获取当前时间的代码时,java 更快

    关于结论 2 的分析

    通过查看源代码,java 的 new Date() 仅执行一个成员赋值
    ```
    public Date() {
    this(System.currentTimeMillis());
    }
    public Date(long date) {
    fastTime = date;
    }
    private transient long fastTime;
    ```
    而 C#的 Date.Now 源码
    ```
    public static DateTime Now
    {
    get
    {
    DateTime utc = UtcNow;
    long offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out bool isAmbiguousLocalDst).Ticks;
    long tick = utc.Ticks + offset;
    if ((ulong)tick <= MaxTicks)
    {
    if (!isAmbiguousLocalDst)
    {
    return new DateTime((ulong)tick | KindLocal);
    }
    return new DateTime((ulong)tick | KindLocalAmbiguousDst);
    }
    return new DateTime(tick < 0 ? KindLocal : MaxTicks | KindLocal);
    }
    }
    public static unsafe DateTime UtcNow
    {
    get
    {
    ulong fileTimeTmp; // mark only the temp local as address-taken
    s_pfnGetSystemTimeAsFileTime(&fileTimeTmp);
    ulong fileTime = fileTimeTmp;

    if (s_systemSupportsLeapSeconds)
    {
    // Query the leap second cache first, which avoids expensive calls to GetFileTimeAsSystemTime.

    LeapSecondCache cacheValue = s_leapSecondCache;
    ulong ticksSinceStartOfCacheValidityWindow = fileTime - cacheValue.OSFileTimeTicksAtStartOfValidityWindow;
    if (ticksSinceStartOfCacheValidityWindow < LeapSecondCache.ValidityPeriodInTicks)
    {
    return new DateTime(dateData: cacheValue.DotnetDateDataAtStartOfValidityWindow + ticksSinceStartOfCacheValidityWindow);
    }

    return UpdateLeapSecondCacheAndReturnUtcNow(); // couldn't use the cache, go down the slow path
    }
    else
    {
    return new DateTime(dateData: fileTime + (FileTimeOffset | KindUtc));
    }
    }
    }
    ```

    可以看出 java 的实现在仅 new Date()的情况下,执行的操作比 C#少很多,诸如 getTime 等都是调用时才计算,而 C#这边则是已经需要计算很多成员所以更慢.
    thinkershare
        11
    thinkershare  
       2022-09-03 14:23:53 +08:00
    @Maboroshii .NET 6.0/7.0 在所有带 runtime 的机器中,包括 go, 我想不到 web 这块, 有哪门平台比.NET 高. 你可以去专业的 Benchmark 平台自己看看. 性能更好的前面都是些 rust/c/c++这种, go 在 web 项目上也没有性能优势.
    另外.NET 的标准库是 NuGet 包, nuget.org, dotnet sdk/vscode/visual studio 的提供了安装依赖的方法. 绝大部分 github 都会提供 NuGet 包给最终用户使用.
    thinkershare
        12
    thinkershare  
       2022-09-03 14:25:34 +08:00
    @nightwitch 我用的是一个开源字体 Iosevka, 主要是为了它的连字符功能, 这个字体其实很丑, 好在它是严格等宽字体, 中文是严格的 2 被英文字体间距.
    Maboroshii
        13
    Maboroshii  
       2022-09-03 15:57:26 +08:00 via Android
    @thinkershare 性能问题我才写了测试,循环计算圆形面积 2000w 次,睡眠 50ms ,c#的 cpu 占用比 go 高很多
    thinkershare
        14
    thinkershare  
       2022-09-03 16:17:34 +08:00   ❤️ 2
    @Maboroshii 找个专业的完整性能评测网站去看看, 专业的性能测试是怎么写的. 性能测试是个非常麻烦的事情, 不是你简单的跑个 demo 就能搞清楚的. 有各种详细的指标. .NET 6 有完整的堆栈控制能力, C#提供了完整的指针控制能力, 大量的为性能而设计的库, 如果都在编译为机器的原生指令和后(JIT 完成后), 除了库本身实现的性能差异, 我想不到你使用 go 写的代码性能会比 C#高非常, 即便使用 C 写, 也不会高出太多, 另外, 说到性能测试, 至少需要像我上面那样将代码贴出来. 在你对两门语言掌握的熟练度相差很大的时候, 你写出来的代码中的某一种很可能不是最优解, 很大可能是你的写法导致了性能巨大的差异.
    a33291
        15
    a33291  
       2022-09-03 16:19:27 +08:00   ❤️ 2
    @Maboroshii 可以发一下测试代码,对于基础的使用,请参阅微软的官网文档,很详细了.

    不管什么语言,大部分时候如果性能不符合预期,都先从自己身上找问题.写语言和编译器的人水平在绝大部分人之上
    jiangzm
        16
    jiangzm  
       2022-09-03 16:29:37 +08:00
    这标题太唬人
    PendingOni
        17
    PendingOni  
       2022-09-03 17:12:22 +08:00
    @a33291 想到我之前线上的一个写的一个 Bug,List 改成 IQueryable 之后查询慢了不是一星半点儿 orz...
    leeg810312
        18
    leeg810312  
       2022-09-03 17:29:31 +08:00 via Android
    .net 日志组件自己写?这么多日志组件没有你造的轮子好?
    PendingOni
        19
    PendingOni  
       2022-09-03 17:42:25 +08:00
    Java 的 System.currentMillis()
    .Net 上有 DateTimeOffset.ToUnixTimeSeconds()

    https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset.tounixtimeseconds?view=net-6.0
    reallittoma
        20
    reallittoma  
       2022-09-03 18:26:57 +08:00
    @userforg2021 #2 看不上这些库
    mmdsun
        21
    mmdsun  
       2022-09-03 19:18:22 +08:00   ❤️ 1
    非.net 开发,帮楼主谷歌了一下:long time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()

    .net core 性能应该比 Java 好,常年测评都是接着者 C/C++、rust 后面
    Aloento
        22
    Aloento  
       2022-09-03 19:34:50 +08:00
    这个问题看的我两眼一黑
    今日笑话( 1/1 )
    ysc3839
        23
    ysc3839  
       2022-09-03 20:37:11 +08:00 via Android
    有一说一 Java 计算现实时间差应该用 System.nanoTime()。System.currentTimeMillis()的精度不一定高。
    Maboroshii
        24
    Maboroshii  
       2022-09-03 23:46:02 +08:00
    @a33291
    @thinkershare

    麻烦指导下, 分别都是用 dotnet run , go run 直接跑的,cpu 占用差了 1 倍。

    ```c#
    class Entry
    {
    public static void Main()
    {
    const int CAL_COUNT = 20000000;
    while (true)
    {
    for (var i = 0; i < CAL_COUNT; i++)
    {
    double pi = 3.14;
    double r = 123.456;

    _ = pi * r * r;
    }
    Thread.Sleep(1000 / 20);
    }
    }

    }
    ```

    ```go
    package main

    import (
    "time"
    )

    const (
    CAL_COUNT = 20000000
    )

    func main() {
    for {
    for i := 0; i < CAL_COUNT; i++ {
    r := 123.456
    pi := 3.14
    _ = pi * r * r
    }
    time.Sleep(time.Millisecond * 1000 / 20)
    }
    }
    ```
    yazinnnn
        25
    yazinnnn  
       2022-09-04 08:18:18 +08:00   ❤️ 3
    就是因为你这种人,java boy 才会被误解.jpg
    userforg2021
        26
    userforg2021  
       2022-09-04 09:11:52 +08:00
    @Maboroshii dotnet run -c Release
    a33291
        27
    a33291  
       2022-09-04 09:51:14 +08:00   ❤️ 1
    @Maboroshii
    是的,请分别在 release 模式下编译后,单独运行进行比较
    C# netcore 6.0.400
    ```
    dotnet build -c Release
    ./main.exe
    ```
    cpu 占用 最高 2.95% 最低 1.53% 平均 2.2%
    内存 15.71MB

    go 1.19
    ```
    go build -ldflags "-s -w" main.go
    ./main.exe
    ```
    cpu 占用 最高 3.39% 最低 1.54% 平均 2.5%
    内存 15.75MB

    以上数据均运行多次,取最优

    就仅针对测试代码来说,这些数据表明 c#和 go 性能基本一致.但是要注意,
    1. 这里边其实包含了编译器本身的 buff 加持,即编译器本身的优化也会影响结果,因为测试代码相对简单,极有可能会被编译器优化掉(暂未对比汇编).
    2. go 中 routine 无处不在,通过查看 2 个程序的线程 cycles delta 等参数可知,go 在运行时多个线程均较高,C#版本仅主线程较高.也就是可能他们的调度机制不一样.

    另外说明,我对 go 不了解,对 go 的信息了解不一定正确(如有误请指正).

    结论
    单就以上测试代码来说,我觉得他们是性能持平的(实际场景业务复杂,不好拉平衡量,暂不讨论).

    附 C#的 csproj 文件
    ```
    <Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <OutputPath>.</OutputPath>
    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
    <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
    </PropertyGroup>
    </Project>
    ```
    sunhelter
        28
    sunhelter  
       2022-09-04 10:40:53 +08:00
    @Maboroshii GitHub 的库一般都会附带 NuGet 链接,在项目里引入 NuGet 包直接调用就行了
    iold
        29
    iold  
       2022-09-04 11:03:31 +08:00
    c#,获取当前时间戳
    DateTimeOffset.Now.ToUnixTimeMilliseconds();
    DateTimeOffset.Now.ToUnixTimeSeconds();

    DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
    DateTimeOffset.UtcNow.ToUnixTimeSeconds();
    beyondex
        30
    beyondex  
       2022-09-04 11:24:49 +08:00 via Android
    @a33291 要比较时间,不能比较
    Cpu 高低,cpu 高的有可能是执行速度更快导致的
    a33291
        31
    a33291  
       2022-09-04 11:27:39 +08:00
    @beyondex 你好,是 @Maboroshii 这位朋友先说测试代码存在 cpu 占用差异,所以我这里才比较的 cpu.

    个人看法哈,对于外部因素都基本一样(代码逻辑一致 硬件环境一致)的场景下,可以横向比较一下.
    bdnet
        32
    bdnet  
       2022-09-04 11:28:10 +08:00
    不得不说标题取得好…(啥玩意啊
    hez2010
        33
    hez2010  
       2022-09-04 11:47:34 +08:00 via iPad
    @Maboroshii dotnet run 直接跑是 Debug ,需要加 -c Release ,而 go 默认是 Release 。你在用 .NET 的 Debug 跟 go 的 Release 做比较,当然显得 go 跑得更快占用更低。
    Maboroshii
        34
    Maboroshii  
       2022-09-04 13:45:35 +08:00 via Android
    @beyondex 都带了 sleep ,所以我认为看 cpu 也合理。
    @hez2010
    @a33291
    @userforg2021 确实不知道还有 release 这个差异,受教了。
    thinkershare
        35
    thinkershare  
       2022-09-04 14:31:47 +08:00
    @Maboroshii 就是带了 Sleep 也不能用它来比较程序的耗费时间, .NET 的 Thread.Sleep 从诞生开始就是个错误, 这个 API 一开始就应该被移除. 性能测试至少也需要使用 Stopwatch, 至少要学会控制变量. 你使用了 Sleep 后, 完全没法控制变量. Sleep 在.NET 中是一个精确度非常差的 API.
    clorischan
        36
    clorischan  
       2022-09-04 15:27:20 +08:00   ❤️ 1
    @Maboroshii 循环的耗时就没保证一致, 完全没有参考价值
    就是不算考虑 Sleep 本身的问题, 就当 Sleep 绝对精确好了
    例如:
    程序 A: 循环耗时 150ms, Sleep 50ms, 每轮共计耗时 200ms, CPU 占用 1%
    程序 B: 循环耗时 50ms,Sleep 50ms, 每轮共计耗时 100ms, CPU 占用 2%
    相同时间内 B 计算次数跟 CPU 占用都翻倍了, 那么你认为那个好
    thinkershare
        37
    thinkershare  
       2022-09-04 15:54:30 +08:00   ❤️ 1
    @clorischan 他缺乏对编程语言和库性能评测的基本常识, 看他给出来的代码就知道了, 这样的问题回答毫无意义.
    我现在明白了, 这是一个钓鱼问题, V2EX 时不时就冒出这种奇奇怪怪的, 纯粹为了骗铜币而提出的问题. 一群人(包括我)傻傻的在下面回答. V2EX 上问题的质量人眼可见下降, 现在居然有很多人开始在上面卖水果了...
    ragnaroks
        38
    ragnaroks  
       2022-09-05 12:21:49 +08:00
    我真不行 dotnet 在有 MSDN docs 的加持下会有这么高的上手门槛
    leegradyllljjjj
        39
    leegradyllljjjj  
       2022-09-06 13:14:54 +08:00
    扎蛙人都被扎蛙搞魔怔了,事实上市场里比 java 先进的语言有很多
    bthulu
        40
    bthulu  
    OP
       2022-09-06 16:33:31 +08:00
    @userforg2021 NLOG 好不好用, 你看看 NLOG 新发的 5.0 版本( https://nlog-project.org/2021/08/25/nlog-5-0-preview1-ready.html)的优化改进不就知道了, 修改了那么多的特性, 改了多少默认设置, 足以证明之前版本的 NLOG 并不好用.

    @PendingOni @iold 这些都是要先 New 一个日期实例, DateTimeOffset.UtcNow 也是 New 了一个实例, 这个实例里做了大量的日期设定类操作, 而我仅仅只需要当前时间戳就行了. 上面有位兄弟也给出了代码对比了, 至少 4 倍差距.
    我自己本地联想小新电脑上测试性能差距更大一些, DateTime.Now 和 new Date()各跑一亿次, .net 耗时 3818 毫秒, java 耗时 364 毫秒, 相差 10 倍.
    ------------------------------------------------------
    --------------.net6.0.8 测试
    ------------------------------------------------------
    using System.Diagnostics;

    // 热身
    for (var i = 0; i < 100_000_000; i++)
    {
    var now = DateTime.Now;
    }

    // 开跑
    var watch = Stopwatch.StartNew();
    for (var i = 0; i < 100_000_000; i++)
    {
    var now = DateTime.Now;
    }

    Console.WriteLine(watch.ElapsedMilliseconds);

    耗时 3818 毫秒

    -----------------------------------------------------
    --------------ms jdk11.0.16.1 (ms1.8 的 jdk 官网找不到了)
    -----------------------------------------------------
    import java.util.Date;

    public class Main {
    public static void main(String[] args) {
    // warm up
    for (var i = 0; i < 100_000_000; i++)
    {
    var now = new Date();
    }

    // start test
    long start = System.currentTimeMillis();
    for (var i = 0; i < 100_000_000; i++)
    {
    var now = new Date();
    }
    long end = System.currentTimeMillis();
    System.out.println(end - start);
    }
    }

    耗时 364 毫秒
    -----------------end-----------------------------------
    PendingOni
        41
    PendingOni  
       2022-09-06 18:19:24 +08:00
    ![企业微信截图_16624594047008.png]( https://s2.loli.net/2022/09/06/A5nGKW4QZXg9e1j.png)
    差不多是的,用时秒级,源码上也是不停的 New 出来的
    bthulu
        42
    bthulu  
    OP
       2022-09-06 19:45:47 +08:00
    @PendingOni 你再试试.Now, 比.UtcNow 还要慢一倍以上
    userforg2021
        43
    userforg2021  
       2022-09-07 17:17:20 +08:00
    既然你都看到源码了,那就看下 Java 为什么快吧。

    从 jdk11 中可以看到 Windows 获取时间使用的函数是 GetSystemTimeAsFileTime ;
    https://github.com/openjdk/jdk/blob/jdk-11+28/src/hotspot/os/windows/os_windows.cpp#L917

    再看.net6 ,可以看到.net 在系统支持时会使用更精确的时间函数 GetSystemTimePreciseAsFileTime ;
    https://github.com/dotnet/runtime/blob/v6.0.8/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs#L134

    GetSystemTimePreciseAsFileTime 函数在 win8 以上受支持:
    https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime#requirements

    这是一个慢的点。但是.net 的 DateTime 精度更高。另外.net 还会计算闰秒:
    https://github.com/dotnet/runtime/blob/v6.0.8/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs#L23

    这是第二个慢的点。也是为了精度考虑的。当然你可以说是.net 的“过度设计”,你不需要精确时间。。。那这个确实就需要自己动手了:

    public unsafe static class SlimDateTime
    {
    private const long TicksPerMillisecond = 10000;
    private const long TicksPerSecond = TicksPerMillisecond * 1000;
    private const long TicksPerMinute = TicksPerSecond * 60;
    private const long TicksPerHour = TicksPerMinute * 60;
    private const long TicksPerDay = TicksPerHour * 24;

    private const int DaysTo1601 = 584388;
    private const int DaysTo1970 = 719162;

    private const long FileTimeOffset = DaysTo1601 * TicksPerDay;

    private const ulong KindUtc = 0x4000000000000000;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static DateTime UtcNow()
    {
    var ticks = GetTicks();
    return *(DateTime*)&ticks;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ulong GetTicks() => OperatingSystem.IsWindows() ? Windows.GetTicks() : (ulong)DateTime.UtcNow.Ticks;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ulong UtcNowUnixTimeMilliseconds() => OperatingSystem.IsWindows() ? Windows.UtcNowUnixTimeMilliseconds() : (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

    private static class Windows
    {
    [DllImport("kernel32", EntryPoint = "GetSystemTimeAsFileTime")]
    [SuppressGCTransition]
    public extern static void GetSystemTimeAsFileTime(ulong* time);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ulong GetTicks()
    {
    ulong fileTime;
    GetSystemTimeAsFileTime(&fileTime);
    return fileTime + (FileTimeOffset | KindUtc);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ulong UtcNowUnixTimeMilliseconds()
    {
    ulong fileTime;
    GetSystemTimeAsFileTime(&fileTime);
    return (fileTime - ((DaysTo1970 - DaysTo1601) * TicksPerDay)) / TicksPerMillisecond;
    }
    }
    }

    其他的就不评价了,懒得打字;
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2973 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 14:03 · PVG 22:03 · LAX 06:03 · JFK 09:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.