为什么我的记忆基准会产生奇怪的结果?

时间:2016-03-13 18:42:52

标签: 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断点上,所花费的时间似乎完全成比例。另一个有趣的因素是我使用的本地工作站的行为似乎反映了较慢的遥控器。

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

1 个答案:

答案 0 :(得分:9)

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

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

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

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

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

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

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

总的来说,分析这段代码并不是很有意义。任何可以而且将要发生的事情,你没有一个体面的方式来预测它。或者是一个很好的方法来做任何事情,除了给VM拥有RAM并且不运行其他任何东西:) second 通过数组的分析结果将会很多更加稳定和有意义,操作系统现在不再参与。