2

前几天我的一位同事表示,使用静态类可能会导致多核系统上的性能问题,因为静态实例不能在处理器缓存之间共享。是对的吗?是否有围绕证明此声明的一些基准?该声明是在 .Net 开发(使用 C#)相关讨论的背景下做出的,但在我看来,这听起来像是一个独立于语言和环境的问题。

谢谢你的意见。

4

8 回答 8

14

我会推动你的同事提供数据或至少参考。

问题是,如果你有共享数据,那么你就有了共享数据。无论是通过静态类、单例还是其他方式公开,都不是很重要。如果您首先不需要共享数据,我希望您无论如何都不会拥有静态类。

除此之外,在任何给定的应用程序中,可能存在比静态类中共享数据的处理器缓存更大的瓶颈。

与以往一样,首先编写最明智、可读、可维护的代码——然后找出性能瓶颈并采取相应措施。

于 2008-12-12T12:42:33.227 回答
3

“[a] 静态实例不能在处理器缓存之间共享。对吗?”

这种说法对我来说没有多大意义。每个处理器的专用缓存的关键在于它包含一小块内存的私有副本,因此如果处理器正在执行一些只需要访问该特定内存区域的算法,那么它不必继续运行返回访问外部存储器。如果我们谈论的是静态类中的静态字段,那么这些字段的内存可能都适合一块连续的内存块,而这块内存又将适合单个处理器(或内核)的专用缓存。但他们每个人都有自己的缓存副本——它不是“共享的”。这就是缓存的意义所在。

如果算法的工作集大于缓存,那么它将破坏该缓存。这意味着当算法运行时,它会反复导致处理器从外部存储器中提取数据,因为所有必要的部分都无法立即放入缓存中。但这是一个普遍问题,不适用于静态类。

我想知道您的同事是否实际上不是在谈论性能,而是在谈论如果多个线程正在读取/写入相同的数据时需要应用正确的锁定?

于 2008-12-12T12:58:46.500 回答
3

如果多个线程正在写入该数据,则会出现缓存抖动(一个 CPU 缓存上的写入会使其他 CPU 的缓存无效)。你的朋友在技术上是正确的,但很有可能它不是你的主要瓶颈,所以没关系。

如果多个线程正在读取数据,那么您的朋友就大错特错了。

于 2008-12-12T13:23:50.337 回答
2

如果您不使用任何类型的锁或同步,那么静态与非静态不会对您的性能产​​生任何影响。

如果您正在使用同步,那么如果所有线程都需要获取相同的锁,那么您可能会遇到问题,但这只是静态的副作用,而不是静态方法的直接结果。

于 2008-12-12T12:42:01.153 回答
2

在任何“虚拟机”受控语言(.NET、Java 等)中,此控制都可能委托给底层操作系统,并且可能进一步下放到 BIOS 和其他调度控制。话虽如此,在 .NET 和 Java 这两个巨头中,静态与非静态是内存问题,而不是 CPU 问题。

再次重申sua的观点,对CPU的影响来自于同步和线程控制,而不是对静态信息的访问。

CPU 缓存管理的问题不仅限于静态方法。一次只有一个 CPU 可以更新任何内存地址。虚拟机中的对象,特别是对象中的字段,是指向所述内存地址的指针。因此,即使我有一个可变对象,一次只允许在一个 CPU 上Foo调用Foo 。setBar(true)

话虽如此,.NET 和 Java 的意义在于,在您能够证明自己有问题之前,您不应该花时间去解决这些问题,我怀疑您会这样做。

于 2008-12-12T13:00:41.693 回答
1
  1. 如果您在线程之间共享可变数据,则需要锁或无锁算法(不幸的是,很少可用,有时很难使用)。
    • 拥有很少的、广泛使用的、锁定仲裁的资源会让你陷入瓶颈。
    • 静态数据类似于单实例资源。

所以:

  • 如果许多线程访问静态数据,并且您使用锁进行仲裁,则您的线程将争夺访问权限。

在设计一个高度多线程的应用程序时,尽量使用许多细粒度的锁。拆分您的数据,以便一个线程可以抓取一个并运行它,希望没有其他线程需要等待它,因为它们正忙于处理自己的数据。

于 2008-12-12T14:29:19.097 回答
1

x86 架构实现了缓存窥探,以在写入时保持数据缓存同步,以防它们碰巧缓存相同的东西……并非所有架构都在硬件中这样做,有些架构依赖于软件来确保这种情况永远不会发生。

于 2008-12-12T15:06:01.347 回答
0

即使这是真的,我怀疑你有很多更好的方法来提高性能。当涉及到将静态更改为实例时,对于处理器缓存,您会知道您确实在挑战极限。

于 2008-12-12T13:21:20.357 回答