4

我使用 C# 作为研究工具,并且经常需要运行 CPU 密集型任务,例如优化。从理论上讲,我应该能够通过多线程我的代码来获得很大的性能改进,但在实践中,当我使用与工作站上可用的内核数相同的线程数时,我通常会发现 CPU 仍然只运行在 25最大的 %-50% 中断代码以查看所有线程在做什么强烈表明内存分配是瓶颈,因为大多数线程将等待new语句执行。

一种解决方案是尝试重新设计我的所有代码以提高内存效率,但这将是一项艰巨且耗时的任务。但是,由于我的工作站上有大量内存,我想知道是否可以通过设置不同的线程来回避这个问题,以便它们每个都有自己的私有内存池可供使用。当然,有些对象仍然需要在所有线程之间公开,否则将无法为每个线程指定任务或获取结果。

有谁知道这种方法在 C# 中是否可行,如果可以,我应该怎么做?

4

3 回答 3

6

如果您有内存分配瓶颈,您应该:

  1. 使用“对象池”(如@MartinJames 所说)。在应用程序启动时初始化对象池。对象池应该提高堆分配的性能。

  2. 使用结构(或任何值类型)作为局部变量,因为堆栈分配比堆快得多。

  3. 避免隐式内存分配。例如,当您将项目添加到List<>

    如果 Count 已经等于 Capacity,则通过自动重新分配内部数组来增加 List 的容量,并且在添加新元素之前将现有元素复制到新数组中(来源 MSDN)。

  4. 避免拳击。这个很贵:

    对于简单的分配,装箱和拆箱是计算成本高的过程。当一个值类型被装箱时,一个新的对象必须被分配和构造。在较小程度上,拆箱所需的演员表在计算上也是昂贵的。(来源 MSDN)

  5. 避免捕获变量的 lambda 表达式(因为将为捕获的变量创建新对象)

于 2013-08-06T20:09:12.073 回答
1

这类似于我在服务器中所做的 - 将对象池用于经常使用的类(尽管不是在 C# 中)。

我想,在 C# 中,您可以使用 BlockingCollection。用其中的 T 和 Take() 对象预先填充它,使用它们,然后用 Add() 返回。

这适用于大量且大型的对象(例如服务器数据缓冲区),或具有复杂而冗长的ctors / dtors(例如http接收器/解析器组件) - 弹出/推送此类对象,('因为本质上是指针在 NET 中),关闭/开启队列比不断创建它们然后让 GC 销毁它们要快得多。

注意:从这样的池队列中弹出的对象可能以前已经使用过,可能需要一些显式初始化!

于 2013-08-06T08:18:47.363 回答
0

它不是一个特别的 C# 或 .NET 问题。为了使 CPU 内核以最佳方式运行,它需要其所有数据都在 CPU 缓存中。如果特定数据不在 CPU 缓存中,则会发生缓存故障并且 CPU 处于空闲状态,直到数据从内存中提取到缓存中。

如果您的内存数据碎片过多,缓存故障的机会就会增加。

CLR 进行堆分配的方式对于 CPU 缓存来说更加优化。您不太可能通过自己处理内存分配来获得相同的性能。

于 2013-08-06T07:20:34.513 回答