我使用C#作为研究工具,经常需要运行CPU密集型任务,例如优化。从理论上讲,我应该能够通过多线程化代码来提高性能,但实际上当我使用与工作站上可用内核数量相同的线程数时,我通常会发现CPU仍然只运行在25 %-50%的最大值中断代码以查看所有线程正在做什么强烈建议内存分配是瓶颈,因为大多数线程将等待new
语句执行。
一种解决方案是尝试重新设计我的所有代码,以提高内存效率,但这将是一项庞大而耗时的任务。但是,由于我的工作站上有大量内存,我想知道我是否可以通过设置不同的线程来回避这个问题,这样他们每个人都有自己的私有内存池来工作。当然,一些对象仍然需要在所有线程之间公开,否则将无法为每个线程指定任务或收集结果。
有没有人知道C#中是否可以采用这种方法,如果有的话,我应该怎么做呢?
答案 0 :(得分:5)
如果您有内存分配瓶颈,您应该:
使用“对象池”(如@MartinJames所说)。应用程序启动时初始化对象池。对象池应该提高堆分配的性能。
使用结构(或任何值类型)作为局部变量,因为堆栈分配比堆快得多。
避免隐式内存分配。例如,当您将项目添加到List<>
:
如果Count已经等于Capacity,则List的容量为 增加了自动重新分配内部数组,以及 现有元素在新元素之前复制到新数组 已添加(源MSDN)。
避免拳击。这是非常昂贵的:
关于简单的作业,装箱和拆箱都是 计算上昂贵的过程。当一个值类型被装箱时,一个新的 对象必须分配和构建。在较小程度上,演员 拆箱所需的计算量也很昂贵。 (来源MSDN)
避免使用捕获变量的lambda表达式(因为将为捕获的变量创建新对象)
答案 1 :(得分:1)
这与我在服务器中所做的类似 - 将对象池用于频繁使用的类,(尽管不在C#中)。
我想在C#中你可以使用BlockingCollection。从它预先加载T和Take()对象,使用它们然后用Add()返回。
这适用于众多和大型的对象(例如服务器数据缓冲区),或具有复杂且冗长的ctors / dtors(例如,http接收器/解析器组件) - 弹出/推送此类对象,('在.NET中基本上是指针),关闭/打开队列要比不断创建它们快得多,以后让GC销毁它们。
注意:从这样的池队列中弹出的对象之前可能已被使用过,可能需要一些显式的初始化!
答案 2 :(得分:0)
它不是特别是C#或.NET问题。要使CPU核心以最佳方式运行,它需要将所有数据都放在CPU缓存中。如果特定数据不在CPU缓存中,则会发生缓存故障并且CPU处于空闲状态,直到数据从内存中提取到缓存。
如果您的内存数据碎片太多,则缓存故障的可能性会增加。
CLR进行堆分配的方式对CPU缓存来说更为理想。您不太可能通过自己处理内存分配来实现相同的性能。