相关疑难解决方法(0)

为什么RDTSC不是序列化指令?

英特尔手册的RDTSC指令警告说,当实际执行RDTSC乱序执行可以改变,所以他们建议将在它前面的CPUID指令,因为CPUID将序列指令流(CPUID是永远不会乱序执行).我的问题很简单:如果他们有能力进行序列化指令,他们为什么不进行RDTSC序列化?它的全部要点似乎是获得周期精确的时间.是否存在一种情况,您希望在它之前加上序列化指令?

较新的Intel CPU具有单独的序列化RDTSCP指令.英特尔选择引入一个单独的指令,而不是改变RDTSC的行为,这表明我必须存在一些可能出现故障时序的情况.它是什么?

performance x86 x86-64 cpu-cycles cpu-architecture

23
推荐指数
3
解决办法
3466
查看次数

在x64上使用非临时存储获取/释放语义

我有类似的东西:

if (f = acquire_load() == ) {
   ... use Foo
}
Run Code Online (Sandbox Code Playgroud)

和:

auto f = new Foo();
release_store(f)
Run Code Online (Sandbox Code Playgroud)

您可以很容易地想象使用atomic with load(memory_order_acquire)和store(memory_order_release)的acquire_load和release_store的实现.但是现在如果release_store是用_mm_stream_si64实现的,这是一个非临时写入,而不是针对x64上的其他商店进行排序的?如何获得相同的语义?

我认为以下是最低要求:

atomic<Foo*> gFoo;

Foo* acquire_load() {
    return gFoo.load(memory_order_relaxed);
}

void release_store(Foo* f) {
   _mm_stream_si64(*(Foo**)&gFoo, f);
}
Run Code Online (Sandbox Code Playgroud)

并使用它:

// thread 1
if (f = acquire_load() == ) {
   _mm_lfence(); 
   ... use Foo
}
Run Code Online (Sandbox Code Playgroud)

和:

// thread 2
auto f = new Foo();
_mm_sfence(); // ensures Foo is constructed by the time f is published to gFoo
release_store(f)
Run Code Online (Sandbox Code Playgroud)

那是对的吗?我非常肯定这里绝对需要sfence.但是那个lfence怎么样?是否需要或者简单的编译器障碍对于x64是否足够?例如asm volatile("":::"memory").根据x86内存模型,负载不会与其他负载重新排序.所以根据我的理解,只要存在编译器障碍,acquire_load()必须在if语句中的任何加载之前发生.

c++ multithreading x86-64 lock-free stdatomic

18
推荐指数
1
解决办法
1267
查看次数

使用openmp parallel for loop意外地表现出色

我之前的评论(特别是@Zboson)之后我编辑了我的问题,以提高可读性

我一直采取行动并观察传统观点,即openmp线程的数量应与机器上的超线程数大致匹配,以获得最佳性能.但是,我观察到我的新笔记本电脑采用Intel Core i7 4960HQ,4核 - 8线程的奇怪行为.(请参阅此处的英特尔文档)

这是我的测试代码:

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>

int main() {
    const int n = 256*8192*100;
    double *A, *B;
    posix_memalign((void**)&A, 64, n*sizeof(double));
    posix_memalign((void**)&B, 64, n*sizeof(double));
    for (int i = 0; i < n; ++i) {
        A[i] = 0.1;
        B[i] = 0.0;
    }
    double start = omp_get_wtime();
    #pragma omp parallel for
    for (int i = 0; i < n; ++i) {
        B[i] = exp(A[i]) + sin(B[i]);
    }
    double end = omp_get_wtime(); …
Run Code Online (Sandbox Code Playgroud)

parallel-processing multithreading gcc openmp avx

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

在clang中矢量化一个函数

我正在尝试使用clang根据此clang参考向量化以下函数.它需要一个字节数组的向量,并根据此RFC应用掩码.

static void apply_mask(vector<uint8_t> &payload, uint8_t (&masking_key)[4]) {
  #pragma clang loop vectorize(enable) interleave(enable)
  for (size_t i = 0; i < payload.size(); i++) {
    payload[i] = payload[i] ^ masking_key[i % 4];
  }
}
Run Code Online (Sandbox Code Playgroud)

以下标志传递给clang:

-O3
-Rpass=loop-vectorize
-Rpass-analysis=loop-vectorize
Run Code Online (Sandbox Code Playgroud)

但是,矢量化失败并出现以下错误:

WebSocket.cpp:5:
WebSocket.h:14:
In file included from boost/asio/io_service.hpp:767:
In file included from boost/asio/impl/io_service.hpp:19:
In file included from boost/asio/detail/service_registry.hpp:143:
In file included from boost/asio/detail/impl/service_registry.ipp:19:
c++/v1/vector:1498:18: remark: loop not vectorized: could not determine number
      of loop iterations [-Rpass-analysis]
    return this->__begin_[__n];
                 ^
c++/v1/vector:1498:18: …
Run Code Online (Sandbox Code Playgroud)

c++ vector simd clang++

11
推荐指数
1
解决办法
2948
查看次数

L1内存带宽:使用相差4096 + 64字节的地址,效率下降50%

我想用英特尔处理器实现以下操作的最大带宽.

for(int i=0; i<n; i++) z[i] = x[i] + y[i]; //n=2048
Run Code Online (Sandbox Code Playgroud)

其中x,y和z是浮点数组.我在Haswell,Ivy Bridge和Westmere系统上这样做.

我最初分配了这样的内存

char *a = (char*)_mm_malloc(sizeof(float)*n, 64);
char *b = (char*)_mm_malloc(sizeof(float)*n, 64);
char *c = (char*)_mm_malloc(sizeof(float)*n, 64);
float *x = (float*)a; float *y = (float*)b; float *z = (float*)c;
Run Code Online (Sandbox Code Playgroud)

当我这样做时,我获得了每个系统预期的峰值带宽的大约50%.

峰值计算为frequency * average bytes/clock_cycle.每个系统的平均字节/时钟周期为:

Core2: two 16 byte reads one 16 byte write per 2 clock cycles     -> 24 bytes/clock cycle
SB/IB: two 32 byte reads and one 32 byte write per 2 clock cycles -> …
Run Code Online (Sandbox Code Playgroud)

c memory x86 caching avx

10
推荐指数
2
解决办法
709
查看次数

内存重新排序如何帮助处理器和编译器?

我研究了Java内存模型并看到了重新排序问题.一个简单的例子:

boolean first = false;
boolean second = false;

void setValues() {
    first = true;
    second = true;
}

void checkValues() {
    while(!second);
    assert first;
}
Run Code Online (Sandbox Code Playgroud)

重新排序是非常不可预测和奇怪的.此外,它破坏了抽象.我认为处理器架构必须有充分的理由去做一些对程序员来说太不方便的事情. 这些原因是什么?

有很多关于如何处理重新排序的信息,但我找不到任何关于它为什么需要的信息.在任何地方,人们只会说"这是因为一些性能优势".例如,second之前存储的性能优势是first什么?

您能推荐一些关于此的文章,论文或书籍,或者自己解释一下吗?

java optimization multithreading cpu-architecture compiler-optimization

9
推荐指数
2
解决办法
1861
查看次数

C循环优化有助于最终分配

因此,对于我在计算机系统课程中的最终作业,我们需要优化这些forloops,使其比原始版本更快.使用我们的linux服务器,基本等级不到7秒,完整等级不到5秒.我在这里的代码大约需要5.6秒.我想我可能需要以某种方式使用指针来使它更快,但我不是很确定.任何人都可以提供我的任何提示或选项吗?非常感谢!

QUICKEDIT:文件必须保持50行或更少,我忽略了教师所包含的那些注释行.

#include <stdio.h>
#include <stdlib.h>

// You are only allowed to make changes to this code as specified by the comments in it.

// The code you submit must have these two values.
#define N_TIMES     600000
#define ARRAY_SIZE   10000

int main(void)
{
    double  *array = calloc(ARRAY_SIZE, sizeof(double));
    double  sum = 0;
    int     i;

    // You can add variables between this comment ...
    register double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0, sum5 = 0, …
Run Code Online (Sandbox Code Playgroud)

c optimization loops compiler-optimization debug-mode

8
推荐指数
2
解决办法
5650
查看次数

SSE:跨越页面边界的未对齐加载和存储

我在某处读到了在页面边界旁边执行未对齐的加载或存储之前(例如使用_mm_loadu_si128/ _mm_storeu_si128intrinsics),代码应首先检查整个向量(在这种情况下是16个字节)是否属于同一页面,如果不是,则切换到非向量指令.我知道如果下一页不属于进程,则需要这样做以防止coredump.

但是,如果两个页面都属于进程(例如,它们是一个缓冲区的一部分,并且我知道该缓冲区的大小),该怎么办?我写了一个小的测试程序,它执行了未对齐的加载和跨越页面边界的存储,并没有崩溃.在这种情况下,我是否必须始终检查页面边界,还是足以确保我不会溢出缓冲区?

环境:Linux,x86_64,gcc

c linux sse x86-64 memory-alignment

8
推荐指数
1
解决办法
688
查看次数

推测执行的 CPU 分支是否可以包含访问 RAM 的操作码?

据我了解,当 CPU 推测性地执行一段代码时,它会在切换到推测性分支之前“备份”寄存器状态,以便如果预测结果错误(使分支无用)——寄存器状态将是安全恢复,而不会破坏“状态”。

所以,我的问题是:推测执行的 CPU 分支是否可以包含访问 RAM 的操作码?

我的意思是,访问 RAM 不是“原子”操作——如果数据当前不在 CPU 缓存中,那么从内存中读取一个简单的操作码可能会导致实际的 RAM 访问,这可能会变成一个非常耗时的操作,从 CPU 的角度来看。

如果在推测分支中确实允许这种访问,它是否仅用于读取操作?因为,我只能假设,如果一个分支被丢弃并执行“回滚”,根据它的大小恢复写操作可能会变得非常缓慢和棘手。而且,可以肯定的是,至少在某种程度上支持读/写操作,因为寄存器本身,在某些 CPU 上,据我所知,物理上位于 CPU 缓存上。

所以,也许更精确的表述是:推测执行的一段代码有什么限制?

cpu cpu-architecture speculative-execution

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

为什么序列化指令本来就对管道不友好?

为什么序列化指令本来就对管道不友好?

在这个其他答案上[ 取消对Intel Sandybridge系列CPU中管道的程序进行优化 ]表示:

每次迭代独立计时,甚至比RDTSC重。例如CPUID / RDTSC或进行系统调用的时间函数。序列化指令本质上是管道不友好的。

我认为应该相反。序列化指令对于管道非常有用。例如,

sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
sum = 5 * sum;
Run Code Online (Sandbox Code Playgroud)

组装者 g++ main.cpp -S

addl    %edx, %eax
movl    %eax, -4(%rbp)
movl    -4(%rbp), %edx
movl    %edx, %eax
sall    $2, %eax
addl    %edx, %eax
movl    %eax, -4(%rbp)
movl    -4(%rbp), %edx
movl    %edx, %eax
sall    $2, %eax
addl    %edx, …
Run Code Online (Sandbox Code Playgroud)

c++ optimization x86 assembly serialization

3
推荐指数
1
解决办法
87
查看次数