3

我正在分析处理数百万条消息的两种设计之间的差异。一种设计使用多态,而另一种则不使用——每条消息都将由多态子类型表示。

我已经使用 VTune 分析了这两种设计。高级摘要数据似乎是有道理的——与使用 IF 语句实现的非多态版本相比,多态设计具有更高的“分支错误预测”率、更高的 CPI 和更高的“ICache 未命中”率。

多态设计有一行这样的源代码:

object->virtualFunction();

这被称为数百万次(子类型每次都更改)。由于分支目标错误预测/指令未命中,我预计多态设计会变慢。如上所述,VTune“摘要”选项卡似乎证实了这一点。但是,当我转到源代码行旁边的指标时,绝对没有指标,除了:

  • 已填充的管道插槽总数 -> 退休 -> 一般退休
  • 填充管道槽自我->退休->一般退休
  • 未填充的管道插槽总数 -> 前端绑定 -> 前端带宽 -> 前端带宽 MITE
  • 未填充的管道槽 self -> 前端绑定 -> 前端带宽 -> 前端带宽 MITE

分支预测列都没有数据,指令缓存未命中列也没有?

有人可以评论这是否合理吗?对我来说,它没有——对于一行多态代码,分支目标将不断变化的每条消息,怎么可能没有分支错误预测或指令缓存未命中统计信息?

这不可能是由于编译器优化/内联,因为编译器不知道要优化的对象的子类型。

我应该如何使用 VTune 分析多态性的开销?

4

2 回答 2

2

我将尝试回答问题的第一部分:

有人可以评论这是否合理吗?对我来说,它没有——对于一行多态代码,分支目标将不断变化的每条消息,怎么可能没有分支错误预测或指令缓存未命中统计信息?

这不可能是由于编译器优化/内联,因为编译器不知道要优化的对象的子类型。

实际上,编译器有一种方法可以内联对虚函数的调用,这是一个有趣的技巧,当我了解它时我感到很惊讶。

您可以观看此 Eric Brumer 的演讲以了解更多详细信息,从 22:30 分钟开始,他谈到了间接调用优化。

基本上,编译器不是向该虚函数指针发出简单的跳转指令,而是首先添加一些比较,并且对于某些已知的指针值预测调用的特定虚函数,然后可以将该调用内联到该分支中。在这种情况下,不可预测的指针值跳转变成了简单的比较分支预测,现代 CPU 擅长于此。因此,如果大多数调用将进入相同的特定虚拟函数实现,您可能会看到良好的预测数和低指令缓存未命中数。

我建议研究该函数调用的反汇编。它是诚实地使用 vtable 指针间接跳转到代码,还是通过一些优化来避免 vtable 跳转。

如果编译器没有优化调用,CPU 仍然可以通过某种方式进行推测,请深入研究Branch Target Buffer。例如,如果这个函数在同一类型的对象上的紧密循环中被调用,那么它是否是虚拟的可能并不重要,它的地址可以被预测......

HTH。

于 2014-05-05T22:10:54.210 回答
1

您不会在指令本身上看到分支错误预测,因为样本将在分支之后的下一条指令上“聚合”。

所有非精确事件都一样(_PS最后没有)。只需检查常规代码配置文件就可以很容易地找到它。例如,有更高的可能性会发现CPU_CLK_UNHALTEDsimple 上的样本addimuladd.

为了查看事件发生的“确切”指令,您必须使用精确事件,例如BR_MISP_RETURED.ALL_BRANCHES_PS.

我不能 100% 确定这个“问题”的真实性质,我知道应该可以修复它,但出于某种原因,VTune 采样驱动程序的人不想这样做。我认识一个人在过去 6 年里一直在与这个问题作斗争,我每次检查 asm VTune 配置文件时都会考虑到这一点:)

PS。关于带有虚拟功能的原始测试。我也对其进行了测试,它确实会产生很多分支错误预测。函数指针也是如此。解决它的一种方法是尽可能使用模板类。

于 2014-02-25T14:55:57.400 回答