静态变量会阻碍数据缓存吗?

时间:2019-02-18 08:34:26

标签: c++ c performance caching

Optimizing Software in C++ (Section 7.1)

  

静态数据的优点是可以将其初始化为所需的数据   程序启动前的值。缺点是内存   整个程序执行期间都会占用空间,即使   变量仅在程序的一小部分中使用。 这使数据   缓存效率较低。

在此static的用法除了在静态存储持续时间的确切情况下适用于C和C ++之外,均如此。

谁能阐明为什么静态持续时间变量的数据缓存效率低(或是否低)?这是一个具体的比较:

void foo() {
  static int static_arr[] = {/**/};
}
void bar() {
  int local_arr[] = {/**/};
}

我看不出任何理由说明静态数据与其他任何类型的数据都不一样。在给定的示例中,我认为foo会更快,因为执行堆栈不必加载static_arr,而在bar中,执行堆栈必须加载{{1 }}。在任何一种情况下,如果重复调用这些函数,则local_arrstatic_arr都将被缓存。我错了吗?

3 个答案:

答案 0 :(得分:6)

通常,Agner Fog通常知道他在说什么。

如果我们在 7.1种不同类型的变量存储部分的上下文中阅读引号,我们会在开始时看到他所说的“ 效率较低的缓存”部分:

  

如果数据随机分散在内存中,则数据缓存效果不佳。它是   因此了解变量的存储方式非常重要。存储原则是   对于简单的变量,数组和对象也是如此。

因此,说static变量的缓存效率较低的想法是,存储变量的存储位置被“冷”(不再位于缓存中)的机会大于堆栈存储器的机会,具有自动存储期限的变量将存储在哪里。

考虑到缓存和分页,是数据存储的物理和时间局部性的组合会影响性能。

答案 1 :(得分:4)

该语句是否有意义取决于您的标点方式:

阅读1:

  

静态数据的优点是可以在程序启动之前将其初始化为所需的值。缺点是,即使变量仅在程序的一小部分中使用,整个程序执行仍会占用存储空间。

     

以上所有使数据缓存效率降低。

这是胡说八道。

阅读2:

  

静态数据的优点是可以在程序启动之前将其初始化为所需的值。

     

缺点是在整个程序执行过程中都会占用内存空间...

     

...即使该变量仅在程序的一小部分中使用。在某些情况下,这可能会使数据缓存效率降低。

在这种情况下,静态变量将被分配存储在一个页面中,该页面不总是被交换,或者位于一个很少使用的高速缓存行中。您可能会导致高速缓存未命中,或者从理论上讲,在最坏的情况下可能会导致页面错误(尽管坦白地说,这几天我们可以使用大量物理内存,如果发生这种情况,则可能会遇到更大的问题)。

在您演示的特定情况下,答案将是“取决于”。

是的,static_arr的初始化是一次性的操作,因此可以认为是无成本的。

是的,local_arr的初始化在每次调用函数时发生,但是可能是这样的:

  1. 此初始化很简单,或者
  2. 作为优化过程的一部分,编译器将忽略初始化

通常,除非您有特定的优化想法,否则最好编写明确声明您想要的行为的代码,即:

  • 当变量/数组的值在连续调用该函数后仍可继续使用时,请使用静态变量(具有静态存储持续时间的变量)。

  • 当现有值在函数进入或退出时无意义时,请使用局部变量(严格来说是具有自动存储期限的变量)。

您会发现,在几乎所有情况下,编译器都会在优化通过后执行最高效的操作。

在特定情况下,静态初始化为Better(tm)。在(例如)需要动态分配的缓冲区的情况下。您可能不希望在每次通话时都产生分配/取消分配的费用。您可能希望缓冲区在需要时动态增长,并根据将来的操作可能再次需要内存的情况保持增长。

在这种情况下,变量的实际状态是其分配的缓冲区的大小。因此,状态对于函数的进入和退出很重要,例如:

  std::string const& to_string(json const& json_object)
  {
    static thread_local buffer;               // thread-safe, auto-expanding buffer storage
    buffer.clear();                           // does not release memory
    serialise_to_string(buffer, json_object); // may allocate memory occasionally
    return buffer;
  }

答案 2 :(得分:3)

rustyx的回答对此进行了解释。局部变量存储在堆栈中。当函数返回时释放堆栈空间,并在调用下一个函数时重新使用堆栈空间。局部变量的缓存效率更高,因为相同的存储空间可以一次又一次地被重用,而静态变量则分散在不同的存储器地址中,而这些地址永远都不能再用于其他目的。静态数据是存储在DATA节中(初始化)还是BSS节(未初始化)在这方面没有区别。栈顶空间很可能在整个程序执行期间保持高速缓存,并且会多次重复使用。

另一个优点是可以使用相对于堆栈指针的8位偏移量访问有限数量的局部变量,而静态变量则需要32位绝对地址(在32位x86中)或32位相对地址(在x86-64中)。换句话说,局部变量可以使代码更紧凑,并提高代码缓存和数据缓存的利用率。

// Example
int main () {
  f();
  g();
  return 0;
}

void f() {
   int x; 
   ...
}

void g() {
   int y;  // y may occupy the same memory address as x
   ...
}