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

5443 次点击
所在节点    .NET
43 条回复
mmdsun
2022-09-03 19:18:22 +08:00
非.net 开发,帮楼主谷歌了一下:long time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()

.net core 性能应该比 Java 好,常年测评都是接着者 C/C++、rust 后面
Aloento
2022-09-03 19:34:50 +08:00
这个问题看的我两眼一黑
今日笑话( 1/1 )
ysc3839
2022-09-03 20:37:11 +08:00
有一说一 Java 计算现实时间差应该用 System.nanoTime()。System.currentTimeMillis()的精度不一定高。
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
2022-09-04 08:18:18 +08:00
就是因为你这种人,java boy 才会被误解.jpg
userforg2021
2022-09-04 09:11:52 +08:00
@Maboroshii dotnet run -c Release
a33291
2022-09-04 09:51:14 +08:00
@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
2022-09-04 10:40:53 +08:00
@Maboroshii GitHub 的库一般都会附带 NuGet 链接,在项目里引入 NuGet 包直接调用就行了
iold
2022-09-04 11:03:31 +08:00
c#,获取当前时间戳
DateTimeOffset.Now.ToUnixTimeMilliseconds();
DateTimeOffset.Now.ToUnixTimeSeconds();

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

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

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

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

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

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

© 2021 V2EX