c# - 为什么使用 ThreadLocal 的 Value 成员的本地副本更快,尽管它是一个引用类型?
问题描述
我正在关注并行编程模式第 107 页上的示例:使用 .NET Framework 4 理解和应用并行模式(https://www.microsoft.com/en-us/download/details.aspx?id=19222)。据说使用 ThreadLocal 的 Value 成员的本地副本比使用 Threadlocal.Value 本身更快。我对此进行了测试,确实如此。但为什么?
从代码中可以看出,_vector2.Value 的本地副本保存在 vector2 中,该本地副本用于对所有项目求和。如果您使用
_vector2.Value[i] += _vector1.Value[i]
而不是
vector2[i] += vector1[i]
代码运行,尽管速度较慢。这就是文章中所说的。现在 int[] 是一个引用类型。这意味着当您在 vector2 中进行复制时,您实际上是在复制 ThreadLocal 的 Value 成员中原始 int[] 的引用。注释掉证实了这一点_vector2.Value = vector2
。打印结果保持不变。所以,我认为不需要这个任务。
现在,由于 _vector2.Value 和 vector2 引用相同的数据,使用本地副本 (vector2) 怎么可能更快?在我的测试中快了大约 4 倍。有谁知道我错过了什么?
class ReferenceList
{
const int VECTOR_LENGTH = 100000000;
private ThreadLocal<int[]> _vector1 = new ThreadLocal<int[]>(() => Enumerable.Range(1, VECTOR_LENGTH).ToArray());
private ThreadLocal<int[]> _vector2 = new ThreadLocal<int[]>(() => Enumerable.Range(1, VECTOR_LENGTH).ToArray());
internal void DoWork()
{
int[] vector1 = _vector1.Value;
int[] vector2 = _vector2.Value;
for (int i = 0; i < VECTOR_LENGTH; i++)
{
// This is the fast way (as in the document)
vector2[i] += vector1[i];
// This is the slow way
//_vector2.Value[i] += _vector1.Value[i];
}
// Since int[] is a reference type. This step is not needed, I think. The result is not influenced when commenting out this line
_vector2.Value = vector2;
Console.WriteLine($"Thread-{Thread.CurrentThread.ManagedThreadId} Result: {String.Join(", ", _vector2.Value.Take(10))}");
}
解决方案
vector1
是对数组的直接引用。没有什么比这更快了。
_vector1
不是对数组的直接引用。_vector1.Value
将产生相同的值 - 但根据源代码,需要付出一些努力才能获得该值。因此,每次您要求.Value
您再次接受(执行方法等)性能损失时(即使您知道它会返回相同的值,也需要付出一些努力才能解决)。这忽略了其他相关成本,例如可能减少数据局部性、增加缓存未命中等。
推荐阅读
- android - 源图像的文件名无效 - 无法更改而不丢失消息
- lighthouse - 需要帮助了解多个构建后生成的 Lighthouse 服务器仪表板
- android - 底部导航视图中的片段路由
- mysql - mySql 更新查询返回 ERROR 1242: 1242: 子查询返回多于 1 行
- r - R Shiny:从上传的数据运行动态 For 循环
- postgresql - jsonb 交叉连接未在 Postgres 中返回 N*M 行
- ios - 如何在 XCode 13 中运行 react native 0.63 应用程序?
- vue.js - NuxtJs 和 VueFlip 集成
- php - gcp 托管 PHP 应用程序中的语义 url
- makefile - 为什么 make -j8 的“-j8”部分似乎在 Gitlab CI 中没有任何作用?