解决Windows平台下localtime_s()函数多线程性能差的方法

7

看起来localtime_s()(等同于标准的localtime_r)在MSVC中包含一个关键部分。

为了比较,这里有两个示例应用程序,一个在循环中执行localtime_s,另一个执行gmtime_s

分析显示,在common_localtime_s<__int64>中调用的isindst内存在严重的锁争用:

localtime lock contention

gmtime没有表现出这个问题:

gmtime no lock contention

如果我需要在多线程环境中使用本地时间,有没有什么方法可以解决这个问题,以获得合理的localtime_s性能?


你是否经常调用这个函数,以至于它成为了瓶颈? - Cody Gray
是的,它目前占用了所有经过时间的 70%(64个CPU)。 - rustyx
你有没有考虑从gmtimelocaltime计算偏移量,然后将该偏移量应用于gmtime返回的时间? - NathanOliver
1
锁保护了用于缓存DST计算结果的一些静态变量。对此你无能为力。如果您不能按建议使用偏移量进行操作,则可能需要使用替代实现进行转换。请注意,_localtime64_s也调用__tzset,它也需要许多锁,所以我敢打赌,即使您绕过了_isindst,这些锁仍然会有所影响。 - Dark Falcon
1
但是localtime根据输入的时间戳计算偏移量。如果输入处于DST中,则会添加DST偏移量。我没有看到通过固定偏移量轻松解决这个问题的方法。 - rustyx
显示剩余2条评论
2个回答

2
这里提供一种解决方案:
记录所有时间使用最快的格式。当通过GUI、日志文件或其他方式向用户展示时,再进行本地时间的转换。
由于大多数GUI和日志输出都是单线程的,因此这应该可以消除程序的剩余部分的争用。
如果程序从未向用户呈现数据,则只需以快速时间格式编写并使用后处理工具进行转换或显示即可。

实际上,我最终在内部使用了 gmtime。但是我还通过复制 MS CRT 实现并将所有 static 变量更改为 thread_local(当然还删除了锁)来实现了 localtime_lockfree。不幸的是,由于 MSCRT 许可证禁止共享修改后的源代码,我无法分享结果代码 :( - rustyx

2
由于标准实现使用了锁定,且没有简单的绕过方式,您可能需要使用其他实现。我建议尝试基于 GetTimeZoneInformationForYear 进行实现,该实现会为您提供标准时间和夏令时的UTC偏移量以及夏令时生效期间的日期。您还可以选择为您关心的每一年调用此函数并缓存结果,以供所有线程使用。
由于 gmtime_s 的性能表现良好,我建议您使用它来获取年份。(请注意,这就是 localtime_s 所做的。)减去 GetTimeZoneInformationForYear 提供的适当 Bias 值,然后再次使用 gmtime_s 将其拆分为日期组件。

非常有趣。看起来 MS CRT 中的实现并没有使用这个 API,而是始终使用当前时区(GetTimeZoneInformation),因此用这种方式实际上可能更准确。顺便说一下,CRT 实现还支持 TZ 环境变量作为覆盖。 - rustyx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接