为什么我的记忆基准会给出奇怪的结果?

Des*_*ion 7 c# arrays benchmarking

我最近运行了一些用C#编写的基本基准测试,试图确定一些看似相同的HyperV远程工作站运行速度远远低于其他工作站的原因.他们在我运行的大多数基本测试中的结果完全相同,但基本内存访问基准测试的结果(具体地说,将二维1000x1000双精度数组初始化为0的时间)相差40倍.

为了进一步研究这个问题,我已经进行了其他几个实验来进一步缩小问题的范围.以指数级增加的数组大小运行相同的测试(直到发生OutOfMemoryException)显示各种遥控器之间没有差异,直到数组大小超过1米,然后立即差异大约为40.实际上,测试增量数组大小,初始化时间与数组大小成比例增加,直到阵列大小正好为999999,然后在低速遥控器上,所用时间增加了900%,而在"快速"遥控器上,随着阵列大小的增加,它减少了70% 1000×1000.从那里,它继续按比例扩大.阵列尺寸为1m x 1和1 x 1m时也会出现同样的现象,但程度要小得多(相反,变化为+ 50%和-30%).

有趣的是,将用于实验的数据类型更改为浮点似乎可以完全消除这种现象.在任何测试中,遥控器之间没有差异,并且即使在1000*1000和2000*2000断点上,所花费的时间似乎完全成比例.另一个有趣的因素是我使用的本地工作站的行为似乎反映了较慢的遥控器.

是否有人知道系统配置中的哪些设置可能导致此影响以及如何更改,或者可以采取哪些措施来进一步调试问题?

Han*_*ant 9

你必须记住你真正在测试的是什么.这当然不是.NET程序分配数组元素的能力.这是非常快的,通常进行的是一个大阵列的内存总线频段,通常约为37千兆字节/秒,具体取决于机器的RAM类型,5GB /秒,你可以遇到的最强类型(慢速时钟DDR2开启)一台旧机器).

new关键字仅在需求分页的虚拟内存操作系统(如Windows)上分配地址空间.只是处理器的数字,每4096字节一个.

一旦您第一次开始分配元素,需求分页功能就会启动,您的代码会强制操作系统为阵列分配RAM.数组元素分配触发页面错误,数组中每个4096字节一个错误.或者你的阵列有512个双打.处理页面错误的成本包含在您的测量中.

只有当OS具有准备好使用的零初始化RAM页面时,才能顺利进行.通常需要脂肪半微秒,给予或采取.处理器仍有很多时间,当操作系统更新页面映射时,它将停止运行.请记住,这只发生在第一个元素访问,后续的快速,因为RAM页面仍然可用.通常.

当这样的RAM页面不可用时,这并不是一帆风顺的.然后操作系统必须掠夺一个.在您的案例中,我可以想到多达4个不同的场景:

  • 页面可用但尚未由低优先级零页面线程零初始化.应该快,不需要太多努力.
  • 需要从另一个进程中窃取页面,并且不需要保留该页面的内容.发生以前包含代码的页面.很快也很快.
  • 页面需要被盗,其内容需要保存在页面文件中.例如,发生以前包含数据的页面.一个硬页错误,一个人伤害.磁盘写入发生时,处理器将停止运行.
  • 特定于您的方案,HyperV管理器决定是时候从主机操作系统借用更多RAM.所有以前的项目符号都适用于该操作系统,以及操作系统交互的开销.不知道需要多少开销,也应该是痛苦的.

你打算击中哪一颗子弹是非常非常难以预测的.最重要的是因为它不仅仅涉及您的程序,而且机器上运行的任何其他程序也会影响它.并且存在记忆效应,比如在开始测试之前编写大文件会产生极大的副作用,这是由等待磁盘的文件系统缓存使用的RAM页面引起的.或者另一个具有分配突发并耗尽零页面队列的进程.或者内存总线变得饱和,很容易做到,也可能受到主机操作系统的影响.等等.

它的长短是对这段代码进行分析并不是很有意义.任何可以而且将要发生的事情,你没有一个体面的方式来预测它.或者做一些关于它的好方法,除了给VM提供RAM并且不运行任何其他东西:) 第二次通过数组的分析结果将更加稳定和有意义,操作系统现在不再涉及了.