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

2022-09-03 11:51:03 +08:00
 bthulu

测试环境: 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 我找了很久, 貌似没有这个东西.

5726 次点击
所在节点    .NET
43 条回复
u823tg
2022-09-03 12:27:23 +08:00
上代码啊,可以帮忙跑下。
userforg2021
2022-09-03 12:40:34 +08:00
大佬不先点评一下 Serilog 、NLog 、Microsoft.Extensions.Logging 、log4net 这些怎么不好用?
thinkershare
2022-09-03 12:42:49 +08:00
不知道你在说啥, 你这样提问, 不提供代码, 没有任何意义. .NET 日志库一大堆, 我完全不相信你自己写的日志组件能达到第三方日志组件的水平, 因为你一看对.NET 常规 API 都不熟练.
chouchoui
2022-09-03 12:58:11 +08:00
基于.net 没啥好用的日志组件, 就自己写了》
谢谢 Java boy 今天创造的笑话。
Leviathann
2022-09-03 13:01:59 +08:00
为什么要用一个被官方标记 deprecated 的类
我说的是 date
thinkershare
2022-09-03 13:21:04 +08:00
https://imgur.com/QUQOi6q.jpg ,Java 提供的 Date 类型早就被废弃, 和 DateTime 提供的 API 都没法公平比较. 根本就不应该被使用. .NET 提供的 DateTime 其实也应该被废弃, 使用 DateTimeOffset 替代.
Bingchunmoli
2022-09-03 13:29:47 +08:00
@thinkershare 实际是项目依旧 1.8 ,有些序列化支持 D'ateTime 还需要额外操作,date 实际代码用的还是更多。 还是早点更替把,框架支持跟上
nightwitch
2022-09-03 14:01:19 +08:00
@thinkershare 字体不错,求分享
Maboroshii
2022-09-03 14:07:15 +08:00
.net 性能确实不咋地… 但是最近也需要用.net 开发。
搭车请教一下.net6 怎么用 github 上别人写的库?我拉下来发现.net 版本不一致都不能跑… 是一个一个文件复制到我自己的项目里面用吗
a33291
2022-09-03 14:08:45 +08:00
系统 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
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
2022-09-03 14:25:34 +08:00
@nightwitch 我用的是一个开源字体 Iosevka, 主要是为了它的连字符功能, 这个字体其实很丑, 好在它是严格等宽字体, 中文是严格的 2 被英文字体间距.
Maboroshii
2022-09-03 15:57:26 +08:00
@thinkershare 性能问题我才写了测试,循环计算圆形面积 2000w 次,睡眠 50ms ,c#的 cpu 占用比 go 高很多
thinkershare
2022-09-03 16:17:34 +08:00
@Maboroshii 找个专业的完整性能评测网站去看看, 专业的性能测试是怎么写的. 性能测试是个非常麻烦的事情, 不是你简单的跑个 demo 就能搞清楚的. 有各种详细的指标. .NET 6 有完整的堆栈控制能力, C#提供了完整的指针控制能力, 大量的为性能而设计的库, 如果都在编译为机器的原生指令和后(JIT 完成后), 除了库本身实现的性能差异, 我想不到你使用 go 写的代码性能会比 C#高非常, 即便使用 C 写, 也不会高出太多, 另外, 说到性能测试, 至少需要像我上面那样将代码贴出来. 在你对两门语言掌握的熟练度相差很大的时候, 你写出来的代码中的某一种很可能不是最优解, 很大可能是你的写法导致了性能巨大的差异.
a33291
2022-09-03 16:19:27 +08:00
@Maboroshii 可以发一下测试代码,对于基础的使用,请参阅微软的官网文档,很详细了.

不管什么语言,大部分时候如果性能不符合预期,都先从自己身上找问题.写语言和编译器的人水平在绝大部分人之上
jiangzm
2022-09-03 16:29:37 +08:00
这标题太唬人
PendingOni
2022-09-03 17:12:22 +08:00
@a33291 想到我之前线上的一个写的一个 Bug,List 改成 IQueryable 之后查询慢了不是一星半点儿 orz...
leeg810312
2022-09-03 17:29:31 +08:00
.net 日志组件自己写?这么多日志组件没有你造的轮子好?
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
2022-09-03 18:26:57 +08:00
@userforg2021 #2 看不上这些库

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/877417

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX