【发布时间】:2018-10-21 19:33:40
【问题描述】:
在 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 秒内运行。
在以下情况下行为基本相同:
- 使用经典的小对象而不是长对象[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);
}
}
【问题讨论】:
-
当然内存管理本质上是单线程的。既分配又收集。否则在尝试分配相同的内存时会遇到竞争情况!
-
除了上面所说的以外,不确定它是否也是您要问的另一件事,仅仅因为您产生多个线程并不一定意味着它会运行得更快。创建线程本身就有开销,因此对于非常短的任务,并行运行并不总是会缩短执行时间。
-
我也同意 npinti 所说的。我喜欢说“多线程必须仔细挑选它的问题”。否则,您最终会得到更复杂(容易出错)、使用更多内存并且更慢然后是单个线程的代码。并行减速是一回事(en.wikipedia.org/wiki/Parallel_slowdown)。就像根本不能多线程的问题一样。只需尝试对斐波那契数列进行多线程处理,以便每个数字都由单独的线程创建。
-
@npitni;线程在这里不运行短任务。没有开销。用数学替换内存分配,你会看到的。
-
@BenoitSanchez:如果我用 CPU 绑定操作替换内存绑定操作,它甚至不再是类似的代码了。
标签: c# multithreading memory-management