【问题标题】:C++, ways to benchmark improvements in cache locality?C ++,对缓存局部性改进进行基准测试的方法?
【发布时间】:2010-11-03 11:43:49
【问题描述】:

我有一个类 X 的实现,它有两个指向两条信息的指针。我编写了一个新的实现,类 Y,它只有一个指向结构的指针,该结构包含作为相邻成员的两条信息。 X 和 Y 的方法通常只需要操作其中一条信息,但提供了一个 get() 方法,该方法返回指向第二条的指针(在这种情况下,X 类只返回其指向该条的指针,Y 类返回地址结构的第二个成员)。在正常使用中,对 X 和 Y 方法的调用将穿插调用 get() 并处理返回的第二个部分。

我希望在现实生活中应该有性能改进,因为这两条信息在 Y 类实现中的内存中彼此相邻(因为它们是结构的相邻成员),但我我在我编写的基准测试中没有看到任何差异(在大循环中对 X 和 Y 方法的调用穿插在它们的第二部分上)。我怀疑这是因为在我的测试中,无论哪种情况,一切都适合缓存。我还不想在我的真实应用程序中尝试这个,因为 X 和 Y 的语义在其他与此优化无关的微妙方式上有所不同,并且移植使用的应用程序将是一些工作,这些基准应该有助于证明这样做是合理的首先工作。

由于更好​​的缓存局部性,观察性能差异的最佳方法是什么?如果我在一个等于两次调用之间缓存大小的数组上做一堆虚拟工作就足够了吗?或者我是否想在一个略小于缓存大小的数组上工作,以便对我的类的实例进行工作会导致事情落入和出缓存?我不确定如何编写对编译器优化和不同缓存大小具有鲁棒性的代码。

【问题讨论】:

    标签: c++ performance pointers caching benchmarking


    【解决方案1】:

    如果您使用的是 Linux,那么将 CachegrindKCacheGrind 结合使用可能会更深入地了解您的缓存的行为方式。

    【讨论】:

      【解决方案2】:

      您可以专门设计一个基准来破坏缓存。例如,分配指向的数据块,以确保它们都位于不同的缓存行上(例如,通过使用自定义内存分配器将分配填充到至少几百字节)。然后反复迭代一些太大的对象,即使 L2 缓存也无法容纳所有内容(非常依赖于平台,因为它取决于缓存中的行数,但是 100 万将涵盖大多数架构并且只需要几百兆 RAM总计)。

      这将为您提供从 X 更改为 Y 所带来的性能增益上限。但它通过将 X 的性能降低到低于任何可能的实际使用情况来实现。为了证明你的情况,你需要一个下限估计,而不是上限估计。所以我不确定你会取得多大的成就,除非你发现即使是最坏的情况也没有显着差异,而且你不必费心优化。

      即使您的目标不是 X 的理论上的最坏情况性能,任何旨在超过缓存的基准测试都只是选择 X 性能不佳的任意点,并查看 Y 是否更好。操纵基准以使 Y 看起来不错并不遥远。你的代码在不可靠的基准测试中表现如何并不重要,除非可能是出于营销谎言文学的目的。

      观察实际性能差异的最佳方法是衡量您班级的真实客户。您说“X 和 Y 的语义在与此优化无关的其他细微方面有所不同”,在这种情况下,我只能建议您编写一个与 X only 不同的类 Z此优化,并在您的应用程序中使用它作为比较。

      一旦您的测试试图代表最差的实际使用情况,那么如果您没有看到任何性能差异,则可能没有性能提升。

      综上所述,如果它合乎逻辑(也就是说,它不会使代码变得更加令人惊讶),那么我会提倡尽量减少 C++ 中的堆分配数量,这只是作为一个经验法则。它不会使速度或总内存使用量变得更糟,而且它确实会简化您的资源处理。当然,经验法则并不能证明重写工作代码是合理的。

      【讨论】:

        【解决方案3】:

        如果我对您的情况的理解正确(如果不正确,请纠正我),那就是六分之一,或者六分之一。

        在 X 类中,您需要一个指针查找任一信息。在 Y 类中,第一个需要一个查找,第二个需要两个(获取第一个然后偏移)。这是为了另一个内存访问而牺牲“局部性”。不幸的是,编译器仍然非常擅长浪费总线时间在 RAM 中查找单词。

        如果可能的话,您将通过将两条目标信息直接保存在相关类中(即,每一条都是它自己的类成员),而不是将这些指针用于不必要的间接寻址,从而获得最佳结果。没有看到任何代码,我只能说这么多。

        无论如何,与对类定义中的两个变量进行微优化相比,通过研究应用程序的算法复杂性,您将获得很多更多的性能。还有一个好主意是使用分析工具来(客观地)查看瓶颈在哪里(gprof 在 *nix 系统上很常见)。您是否有明确的原因要专门增加局部缓存?

        【讨论】:

        • 'Why' 在这里并不是真正的问题 - 问题很明显是对缓存位置进行基准测试。我不认为“为什么”真的会在讨论中增加任何内容,最好假设约瑟夫知道他在做什么。
        • “为什么”总是很重要,至少恕我直言。 “我希望在现实生活中应该有性能改进”这告诉我约瑟夫正在寻求加快速度。 “我还不想在我的真实应用程序中尝试这个”,这更强烈地表明他的最终目标是更好的性能,并且他正试图通过改善局部性来实现它——这就是为什么我推荐其他课程来提高性能。但是,@Joseph,如果我在这里走错了方向,请忽略。 ;-) [在这种情况下,cachegrind 就是你想要的]
        • 我正在编写一个基本上没有算法的智能指针类。我已经使用 g-prof 对其进行了优化,直到分支是否存在(if)或虚假整数赋值等可以确定我的类是否优于旧实现的程度。这是微优化肯定适用的少数情况之一;)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-20
        • 2012-02-02
        • 1970-01-01
        • 1970-01-01
        • 2015-08-02
        • 1970-01-01
        相关资源
        最近更新 更多