如果尚未收集 mttest 的性能数据,则您必须先进行收集,然后在性能分析器的两个单独实例中打开两个实验 mttest.1.er 和 mttest.2.er。有关说明,请参见为 mttest 示例收集数据。
在单 CPU 实验 mttest.2.er 中,ComputeA() 的相容用户 CPU 时间与 ComputeB() 的相应时间大致相同。
在四 CPU 实验 mttest.1.er 中,ComputeB() 使用的相容用户 CPU 时间要比 ComputeA() 更多。
剩余指令适用于四 CPU 实验 mttest.1.er。
这些函数的代码相同:使变量增加一的一个循环。所有用户 CPU 时间均花费在此循环中。要了解 ComputeB() 使用的时间多于 ComputeA() 的原因,您必须检查调用这两个函数的代码。
ComputeA() 和 ComputeB() 均由使用指针的引用调用,因此它们的名称不会出现在源代码中。
您可以验证 cache_trash() 是否为 ComputeB() 的调用者,方法是在 [函数] 标签中选择 ComputeB(),然后单击 [调用者-被调用者] 标签。
ComputeA() 由线程工作块中的双精度型作为参数 (&array->list[0]) 进行调用,该参数可直接被读写,而不存在与其它线程竞争的危险。
然而,ComputeB() 由在内存中占用连续字的一系列双精度型 (&element[array->index]) 进行调用。只要有一个线程写入内存中的这些地址其中之一,则任何其它线程(在其缓存中具有该地址)必须删除此时已经过时的数据。如果某个线程稍后在程序中再次需要此数据,则必须将该数据从内存中复制到数据缓存内,即使所需的数据项尚未更改。所造成的缓存未命中,即访问数据缓存中不可用数据的尝试,将浪费大量的 CPU 时间。这就解释了在四 CPU 实验中为何 ComputeB() 使用的用户 CPU 时间要远远多于 ComputeA() 所用的时间。
在单 CPU 实验中,一次只运行一个线程,其它任何线程都无法向内存中写入。运行线程的缓存数据从不会变为无效。不会发生任何缓存未命中或引起的从内存中进行复制的现象,因此在只有一个 CPU 可用时,ComputeB() 的性能和 ComputeA() 的性能同样有效。
如果您使用的计算机具有硬件计数器,请再次运行四 CPU 实验,并为其中一个缓存硬件计数器收集数据,如缓存未命中或延迟周期。在 UltraSPARC III 硬件上,您可以使用命令
% collect -p off -h dcstall -o mttest.3.er mttest
通过选择 [文件] [添加],可以将此新实验中的信息与前一个四 CPU 实验中的信息相合并。在 [函数] 和 [源] 标签中检查 ComputeA 和 ComputeB 的硬件计数器数据。