在更好的系统上多线程性能较差(可能是由于Deedle)

本文关键字:Deedle 系统 更好 多线程 性能 | 更新日期: 2023-09-27 17:56:31

我们正在处理使用Deedle的多线程C#服务。在四核当前系统与八核目标系统上的测试表明,目标系统上的服务速度大约慢两倍,而不是快两倍。即使将线程数限制为两个,目标系统仍然慢了近 40%。

分析显示,Deedle(/F#)需要大量等待,使得目标系统基本上在两个内核上运行。非Deedle测试程序在目标系统上显示正常行为和超强内存带宽。

关于可能导致这种情况的原因或如何最好地处理这种情况的任何想法?

编辑:似乎大多数时间等待都是在调用调用中完成的。

在更好的系统上多线程性能较差(可能是由于Deedle)

问题原来是使用 Windows 7、.NET 4.5(或实际上是 4.0 运行时)和在 F#/Deedle 中大量使用尾递归的组合。

使用 Visual Studio 的并发可视化工具,我已经发现大部分时间都花在调用调用上。仔细检查,这些结果会产生以下调用跟踪:

ntdll.dll:RtlEnterCriticalSection
ntdll.dll:RtlpLookupDynamicFunctionEntry
ntdll.dll:RtlLookupFunctionEntry
clr.dll:JIT_TailCall
<some Deedle/F# thing>.Invoke

搜索这些函数给出了多篇文章和论坛主题,表明使用 F# 可能会导致大量调用JIT_TailCall,并且 .NET 4.6 具有新的 JIT 编译器,该编译器似乎可以处理与这些调用相关的一些问题。虽然我没有发现任何提到与锁定/同步相关的问题,但这确实让我觉得更新到 .NET 4.6 可能是一种解决方案。

但是,在我自己的也使用 .NET 4.5 的 Windows 8.1 系统上,不会出现此问题。在搜索了类似的 Invoke 调用后,我发现此系统上的调用跟踪如下所示:

ntdll.dll:RtlAcquireSRWLockShared
ntdll.dll:RtlpLookupDynamicFunctionEntry
ntdll.dll:RtlLookupFunctionEntry
clr.dll:JIT_TailCall
<some Deedle/F# thing>.Invoke

显然,在Windows 8(.1)中,锁定机制更改为不那么严格的机制,从而减少了等待锁定的需要。

因此,只有当目标系统将 Windows 7 的严格锁定和 .NET 4.5 效率较低的 JIT 编译器相结合时,F# 大量使用尾递归才会导致问题。更新到 .NET 4.6 后,问题消失了,我们的服务按预期运行。