Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET4垃圾收集器的可伸缩性_C#_F#_Garbage Collection_Multicore - Fatal编程技术网

C# .NET4垃圾收集器的可伸缩性

C# .NET4垃圾收集器的可伸缩性,c#,f#,garbage-collection,multicore,C#,F#,Garbage Collection,Multicore,我最近对.NET4垃圾收集器进行了基准测试,从多个线程集中分配垃圾。当分配的值被记录在一个数组中时,我没有观察到像我预期的那样的可伸缩性(因为系统争用对共享旧一代的同步访问)。然而,当分配的值立即被丢弃时,我也震惊地发现没有可伸缩性 我曾期望这个临时案例几乎是线性扩展的,因为每个线程都应该简单地擦除托儿所gen0,然后重新开始,而不必争夺任何共享资源(没有任何东西可以保存到老一代,也没有二级缓存丢失,因为gen0很容易适合一级缓存) 例如: 无同步分配在多处理器系统上,托管堆的第0代使用每个线程

我最近对.NET4垃圾收集器进行了基准测试,从多个线程集中分配垃圾。当分配的值被记录在一个数组中时,我没有观察到像我预期的那样的可伸缩性(因为系统争用对共享旧一代的同步访问)。然而,当分配的值立即被丢弃时,我也震惊地发现没有可伸缩性

我曾期望这个临时案例几乎是线性扩展的,因为每个线程都应该简单地擦除托儿所gen0,然后重新开始,而不必争夺任何共享资源(没有任何东西可以保存到老一代,也没有二级缓存丢失,因为gen0很容易适合一级缓存)

例如:

无同步分配在多处理器系统上,托管堆的第0代使用每个线程一个竞技场拆分为多个内存竞技场。这允许多个线程同时进行分配,因此不需要独占访问堆

有人能证实我的发现和/或解释我的预测和观察之间的差异吗

或者解释我的预测和观察之间的差异

基准测试很难。

对不在您完全控制之下的子系统进行基准测试更加困难

不太确定这是关于什么以及您在机器上看到的确切内容。但是,您的计算机上有两个不同版本的CLR。Mscorwks.dll和mscorsvc.dll。前者是在工作站上运行程序时得到的,后者是在Windows的一个服务器版本(如Windows 2003或2008)上得到的

工作站版本对本地PC很友好,不会占用所有机器资源。当GC正在进行时,您仍然可以阅读您的电子邮件。服务器版本经过优化,可在服务器级硬件上扩展。大量的RAM(GC没有那么快启动)和大量的CPU内核(垃圾收集在多个内核上)。你引用的文章可能谈到了服务器版本


您可以在工作站上选择服务器版本,使用.config文件中的
元素。

我可以冒险猜测一下发生了什么

(1) 如果您只有一个线程,并且第0代中有M个可用空间,那么GC将只在分配了M个字节后运行

(2) 如果您有N个线程,并且GC将第0代划分为每个线程的N/M空间,那么每次线程分配N/M字节时,GC都会运行。这里要说明的是GC需要“停止世界”(即挂起所有正在运行的线程),以便标记来自线程根集的引用。这不便宜。因此,GC不仅会更频繁地运行,而且会在每个集合上做更多的工作

当然,另一个问题是多线程应用程序通常不是非常友好的缓存,这也会显著降低性能


我不认为这是一个.NETGC问题,而是GC的一般问题。一位同事曾经运行过一个简单的“乒乓”基准测试,使用SOAP在两个线程之间发送简单的整数消息。当两个线程处于不同的进程中时,基准测试的运行速度是原来的两倍,因为内存分配和管理是完全解耦的

这不是问题的完整答案,只是为了澄清一些误解:.NET GC只在工作站模式下是并发的。在服务器模式下,它使用停止世界并行GC。更多细节。NET中的独立托儿所主要是为了避免分配上的同步;尽管如此,它们仍然是全局堆的一部分,不能单独收集。

非常快速、易于查看(直接从根开始,分配空值)和大规模发布可以诱使GC变得急切,缓存本地堆的整个想法是一个美好的梦想:-)即使您有完全分离的线程本地堆(您没有)句柄指针表仍然必须是完全可变的,以确保在一般多CPU情况下是安全的。哦,请记住,有很多线程,CPU缓存是共享的,内核需要优先考虑,所以这不仅仅是为您准备的:-)

还要注意的是,带有双指针的“heap”有两个部分——要提供的内存块和句柄指针表(这样块可以移动,但代码总是有一个地址)。这样的表是一个关键但非常精简的流程级资源,强调它的唯一方法就是大量快速发布-因此您成功地做到了:-)

一般来说,GC的规则是-leak:-)当然不是永远,而是尽可能长的时间。如果你还记得人们是怎么说“不要强迫GC收集”的吗?这就是故事的一部分。此外,“停止世界”集合实际上比“并发”集合效率更高,过去被称为循环窃取或调度合作。只有标记阶段需要冻结调度程序,并且在服务器上有多个线程执行此操作(N个内核都处于空闲状态:-)另一个阶段的唯一原因是它可以使实时操作(如播放视频)变得不稳定,就像较长的线程quantum所做的那样

因此,如果您在短时间和频繁的CPU突发(小alloc、几乎不工作、快速发布)上与基础设施竞争,您将看到/测量的唯一东西将是GC和JIT噪声

如果这是为了一些真实的东西,也就是说,不仅仅是实验,那么最好的方法就是在堆栈(structs)上使用大值数组。它们不能被强制到堆上,并且是本地的,不受任何后门移动的影响=>缓存必须喜欢它们:-)这可能意味着切换到“不安全”模式,使用普通指针,或者自己做一点alloc(如果yopu需要类似列表的简单操作)但这是将GC踢出的一个小代价:-)尝试将数据强制放入缓存也取决于保持y