Thread-safe initialization of function-local static const objects

后端 未结 8 1501
悲&欢浪女
悲&欢浪女 2020-12-28 16:41

This question made me question a practice I had been following for years.

For thread-safe initialization of function-local static const objects I protect t

8条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-12-28 17:00

    I've programmed enough interprocess sockets to have nightmares. In order to make anything thread-safe on a CPU with DDR RAM, you have to cache-line-align the data structure and pack up all of your global variables contiguously into as few cache lines as possible.

    The problem with unaligned interprocess data and loosely packed globals are that it causes aliasing from cache misses. In CPUs that use DDR RAM, there are a (usually) a bunch of 64-byte cache lines. When you load a cache line, the DDR RAM will automatically load up a bunch more cache lines, but the first cache line is always the hottest. What happens with interrupts that occur at high speeds is that the cache page will act as a low-pass filter, just like in analog signals, and will filter out the interrupt data leading to COMPLETELY baffling bugs if you're not aware of whats going on. That same thing goes for global variables that are not packed up tightly; if it takes up multiple cache lines it will get out of sync unless you to take a snapshot of the critical interprocess variables and pass them through on the stack and the registers to ensure the data is synced up right.

    The .bss section (i.e. where the global variables are stored, will get initialized to all zeros, but the compiler will not cache-line-align the data for you, you will have to do that yourself, which may also a good place to use the C++ Construct in Place. To learn the math behind fastest way to align pointers read this article; I'm trying to figure out if I came up with that trick. Here is what the code will look like:

    inline char* AlignCacheLine (char* buffer) {
      uintptr_t offset = ((~reinterpret_cast (buffer)) + 1) & (63);
      return buffer + offset;
    }
    
    char SomeTypeInit (char* buffer, int param_1, int param_2, int param_3) {
      SomeType type = SomeType (1, 2, 3);
      return 0xff;
    }
    
    const SomeType* create_const_thingy () {
      static char interprocess_socket[sizeof (SomeType) + 63],
                  dead_byte = SomeTypeInit (interprocess_socket, 1, 2, 3);
      return reinterpret_cast (AlignCacheLine (interprocess_socket));
    }
    

    In my experience, you will have to use a pointer, not a reference.

提交回复
热议问题