小编Ren*_* R.的帖子

缓存行,错误共享和对齐

我编写了以下简短的C++程序来重现Herb Sutter所描述的错误共享效果:

比如说,我们想要执行总量的WORKLOAD整数运算,并且我们希望它们平均分配给多个(PARALLEL)线程.出于此测试的目的,每个线程将从整数数组中递增其自己的专用变量,因此该过程可以理想地并行化.

void thread_func(int* ptr)
{
    for (unsigned i = 0; i < WORKLOAD / PARALLEL; ++i)
    {
        (*ptr)++;
    }
}

int main()
{
    int arr[PARALLEL * PADDING];
    thread threads[PARALLEL];

    for (unsigned i = 0; i < PARALLEL; ++i)
    {
        threads[i] = thread(thread_func, &(arr[i * PADDING]));
    }
    for (auto& th : threads)
    {
        th.join();
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我认为这个想法很容易理解.如果你设置

#define PADDING 16
Run Code Online (Sandbox Code Playgroud)

每个线程将在单独的缓存行上工作(假设缓存行的长度为64字节).因此,结果将是加速的线性增加,直到PARALLEL> #core.另一方面,如果将PADDING设置为低于16的任何值,则应该遇到严重的争用,因为现在至少有两个线程可能在相同的高速缓存行上运行,但是受到内置硬件互斥锁的保护.我们希望我们的加速不仅在这种情况下是次线性的,而且即使总是<1,因为看不见的锁定车队.

现在,我的第一次尝试几乎满足了这些期望,但PADDING避免错误共享所需的最小值是8左右而不是16分.在我得出明显结论之前,我很困惑半小时,我无法保证我的数组与主内存中的缓存行的开头完全对齐.实际对齐可能根据许多条件而变化,包括阵列的大小.

在这个例子中,当然没有必要让我们以特殊的方式对齐数组,因为我们可以将PADDING保持在16并且一切正常.但人们可以想象一下案例,它确实会产生影响,某个结构是否与缓存行对齐.因此,我添加了一些代码行来获取有关数组实际对齐的一些信息.

int main()
{
    int arr[PARALLEL * 16];
    thread threads[PARALLEL];
    int offset …
Run Code Online (Sandbox Code Playgroud)

c++ parallel-processing multithreading caching

12
推荐指数
1
解决办法
7111
查看次数

如何演示指令缓存限制的影响

我的想法是给出一个优雅的代码示例,它将演示指令缓存限制的影响.我编写了以下代码,使用模板元编程创建了大量相同的函数.

volatile int checksum;
void (*funcs[MAX_FUNCS])(void);

template <unsigned t> 
__attribute__ ((noinline)) static void work(void) { ++checksum; }

template <unsigned t> 
static void create(void) { funcs[t - 1] = &work<t - 1>; create<t - 1>(); }

template <> void create<0>(void) {  }

int main()
{
    create<MAX_FUNCS>();

    for (unsigned range = 1; range <= MAX_FUNCS; range *= 2)
    {
        checksum = 0;
        for (unsigned i = 0; i < WORKLOAD; ++i)
        {
            funcs[i % range]();
        }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

外循环使用跳转表改变要调用的不同函数的数量.对于每个循环传递,WORKLOAD …

c++ performance caching template-meta-programming branch-prediction

6
推荐指数
1
解决办法
336
查看次数

GCC内联汇编中的一个简单while循环

我想使用GCC扩展内联ASM编写以下循环:

long* arr = new long[ARR_LEN]();
long* act_ptr = arr;
long* end_ptr = arr + ARR_LEN;

while (act_ptr < end_ptr)
{
    *act_ptr = SOME_VALUE;
    act_ptr += STEP_SIZE;
}

delete[] arr;
Run Code Online (Sandbox Code Playgroud)

long具有长度的类型的数组ARR_LEN被分配并初始化为零。循环以的增量遍历数组STEP_SIZE。每个感动的元素都设置为SOME_VALUE

好吧,这是我第一次在GAS中尝试:

long* arr = new long[ARR_LEN]();

asm volatile
(
    "loop:"
    "movl %[sval], (%[aptr]);"
    "leal (%[aptr], %[incr], 4), %[aptr];"
    "cmpl %[eptr], %[aptr];"
    "jl loop;"
    : // no output
    : [aptr] "r" (arr),
      [eptr] "r" (arr + ARR_LEN),
      [incr] "r" (STEP_SIZE),
      [sval] "i" (SOME_VALUE) …
Run Code Online (Sandbox Code Playgroud)

c++ gcc gnu-assembler inline-assembly

5
推荐指数
1
解决办法
4858
查看次数

为什么我的二进制文件的大小取决于我的数组的大小?

我最近偶然发现了一个非常奇怪的代码膨胀效应,我真的无法向自己解释......以下是一个有用的最小例子:

#include <array>

const int SIZE = 4000000;

struct Foo
{
    static Foo& GetInstance()
    {
        static Foo instance;
        return instance;
    }

    std::array<float, SIZE> Bar;
};

int main()
{
    Foo::GetInstance().Bar[0] = 1.0f;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

生成的二进制文件(使用GCC MinGW 4.9.2 x86_64 posix sjlj构建)的大小为15.28 MB.但是,如果设置为eg,SIZE = 1则会获得17 KB二进制文件.

那么为什么二进制的大小取决于数组的大小呢?显然,效果是由结构为单身人士引起的.然而,我仍然没有看到任何合理的推理,为什么编译器会膨胀二进制文件.谢谢你的帮助!

(使用和不使用优化测试并-std=c++11仅使用标志.顺便说一下,这也适用于C风格的数组...)

c++ arrays mingw c++11 stdarray

4
推荐指数
1
解决办法
237
查看次数