Windows 上 localtime_s() 多线程性能不佳的解决方法

Posted

技术标签:

【中文标题】Windows 上 localtime_s() 多线程性能不佳的解决方法【英文标题】:Workaround for poor multithreaded performance of localtime_s() on Windows 【发布时间】:2016-03-02 14:46:38 【问题描述】:

似乎localtime_s()(相当于标准localtime_r)在MSVC 中包含一个临界区。

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

http://rextester.com/OQJ48177 http://rextester.com/JNDR45936

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

gmtime 没有出现问题:

如果我的进程确实需要本地时间,是否有任何方法可以解决此问题以在多线程环境中获得稳定的localtime_s 性能?

【问题讨论】:

你真的经常调用这个函数以至于它是一个瓶颈吗? 是的,它目前占用了所有已用时间的 70%(64 个 CPU)。 是的,我也遇到过同样的问题。 localtime_s 在多线程环境中很慢。我无法找到解决方案。 锁保护了一些用于缓存DST计算结果的静态变量。你对此无能为力。如果您无法按照建议处理偏移量,您可能必须使用转换的替代实现。请注意,_localtime64_s 也调用了__tzset,它也需要一些锁,所以我敢打赌,即使你在_isindst 周围工作,它们仍然会咬你一些。 但是localtime 根据输入时间戳计算偏移量。如果输入位于 DST 中,则它会添加 DST 偏移量。没有一种简单的方法可以使用固定偏移量来解决这个问题。 【参考方案1】:

这是一个建议的解决方案:

以最快的速度记录所有时间。当通过 GUI、日志文件或其他方式将它们呈现给用户时,请转换为本地时间。

由于大多数 GUI 和日志输出都是单线程的,这应该消除程序其余部分的争用。

如果程序从不向用户呈现数据,那么只需将其以快速时间格式写出来,然后使用后处理工具对其进行转换或显示。

【讨论】:

确实,我最终在内部使用了gmtime。但我也通过复制 MS CRT 实现并将所有static 变量设置为thread_local 来实现localtime_lockfree(当然还要移除锁)。不幸的是,我无法共享生成的代码,因为 MSCRT 许可证禁止共享修改后的源:(【参考方案2】:

由于标准实现使用锁并且没有简单的解决方法,您可能不得不使用不同的实现。我建议尝试以GetTimeZoneInformationForYear 为基础,这将为您提供标准和 DST 时间以及 DST 处于活动状态的日期与 UTC 的偏移量。您还可以选择为您关心的每一年调用一次,并缓存结果以供所有线程使用。

由于gmtime_s 的表现可以接受,我建议您使用它来获取年份。 (注意这是localtime_s 所做的。)减去GetTimeZoneInformationForYear 提供的适当偏差值,然后再次使用gmtime_s 将其拆分为日期组件。

【讨论】:

非常有趣。似乎 MS CRT 中的实现不使用此 API,但始终使用当前时区 (GetTimeZoneInformation),因此这样做实际上可以更准确。顺便说一句,CRT 实现还支持 TZ 环境变量作为覆盖。

以上是关于Windows 上 localtime_s() 多线程性能不佳的解决方法的主要内容,如果未能解决你的问题,请参考以下文章

C语言关于localtime_s()和asctime_s()两个函数的用法。

localtime vs localtime_s 和适当的输入参数

vs2013写c++程序,其中localtime_s函数不接受一个参数怎么解决?!

C语言编程函数localtime_s在VS 2020中写的程序参数错误,参数怎么写?

为啥在windows上使用python多处理时打印三遍?

Pyautogui 在屏幕上定位在 Windows 10 上花费了太多时间