c# - 分配内存时的线程争用
问题描述
在 C# 中,我运行了一个创建许多小对象的玩具代码(我知道理想情况下应该避免这种情况——我只是想研究这个问题)。对于相同数量的创建对象,一个线程比每个处理器一个线程运行得更快 (Parallel.For)。
原子操作包括创建一个包含 20k 小对象的列表(实际上是一个数组)(为简单起见,此处为 long[4]):
private static void CreateList()
{
long[][] list = new long[20000][];
for (var i = 0; i < 20000; i++)
list[i] = new long[4];
}
如果我在单个线程中创建 1000 个列表,它会在 1.5 秒内运行。如果我用多个线程创建 1000 个列表(每个线程负责 1000 个列表的一个子集),它会在 2 秒内运行。
在以下情况下,行为基本相同:
- 使用经典的小对象而不是 long[4]
- 使用真正的 List 而不是数组
- 使用不同数量的对象
你能解释一下为什么吗?内存管理器中是否有“锁”。它与垃圾收集有关吗?
代码详情:
public static void Main()
{
Benchmark(1000, CreateList);
}
private static void Benchmark(int repeat, Action action)
{
Console.WriteLine("Single thread");
Benchmark(delegate ()
{
for (int i = 0; i < repeat; i++)
action();
});
Console.WriteLine("Multi thread");
Benchmark(delegate ()
{
Parallel.For(0, repeat, i => action());
});
}
private static void Benchmark(Action action)
{
for (int i = 0; i < 10; i++)
{
Stopwatch sw = new Stopwatch();
sw.Start();
action();
sw.Stop();
Console.WriteLine("Time : " + sw.Elapsed.TotalSeconds);
}
}
解决方案
尽管内存管理器使用某种信号量是正常的,但具有许多内存分配的多线程应用程序在默认的 C# 垃圾收集器中工作得非常糟糕。使用适当的垃圾收集器,事情会好得多。
你应该:
- 启用服务器 GC
- (可能)禁用并发 GC
服务器 GC 将允许线程之间更好程度的并行化,因为内存分配是部分独立的。在这种情况下,多核机器的性能会发生根本性的变化。
简而言之,将其添加到项目的配置文件中:
<runtime>
<gcServer enabled="true"/>
<gcConcurrent enabled="false" />
</runtime>
您可以在垃圾收集基础中阅读有关服务器和工作站 GC 的详细信息。
推荐阅读
- java - J2SE和J2EE中的2代表什么?
- python - 'array[:] = something' 和 'array = something' 之间的区别
- python - 具有已定义名称范围的 Pandas 数据框到 Excel
- accessibility - 辅助功能:相关内容 - 两指轻弹
- sql - 分配一个唯一的 id sql server
- sql - 如何根据行号动态创建新列
- vue.js - 无法从 rootState 访问 Vuex 模块
- r - 如何使用windows scheduler执行R脚本
- java - 是否可以抽象出重复的 XML 代码部分?
- excel - 删除折线图中的刻度线,APACHE POI