在Linux上,如果我去的话malloc(1024 * 1024 * 1024)
,malloc实际上做了什么?
我确定它为分配分配了一个虚拟地址(通过遍历空闲列表并在必要时创建新映射),但它实际上是否创建了1 GiB的交换页面?或者它mprotect
是地址范围并在您实际触摸它们时创建页面mmap
吗?
(我正在指定Linux,因为标准对这些细节没有提及,但我有兴趣知道其他平台也会这样做.)
我想使用增强的REP MOVSB(ERMSB)为自定义获得高带宽memcpy
.
ERMSB引入了Ivy Bridge微体系结构.如果您不知道ERMSB是什么,请参阅英特尔优化手册中的"增强型REP MOVSB和STOSB操作(ERMSB)" 部分.
我知道直接执行此操作的唯一方法是使用内联汇编.我从https://groups.google.com/forum/#!topic/gnu.gcc.help/-Bmlm_EG_fE获得了以下功能
static inline void *__movsb(void *d, const void *s, size_t n) {
asm volatile ("rep movsb"
: "=D" (d),
"=S" (s),
"=c" (n)
: "0" (d),
"1" (s),
"2" (n)
: "memory");
return d;
}
Run Code Online (Sandbox Code Playgroud)
然而,当我使用它时,带宽远小于memcpy
.
使用我的i7-6700HQ(Skylake)系统,Ubuntu 16.10,DDR4 @ 2400 MHz双通道32 GB,GCC 6.2,__movsb
获得15 GB/s并memcpy
获得26 GB/s.
为什么带宽如此低REP MOVSB
?我该怎么做才能改善它?
这是我用来测试它的代码.
//gcc -O3 -march=native -fopenmp foo.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include …
Run Code Online (Sandbox Code Playgroud) 在大多数处理器中,为什么L1缓存的大小小于L2缓存的大小?
我看到一个简单的存储循环出乎意料地表现不佳,这个存储循环有两个存储:一个具有16字节的正向步长,另一个总是位于同一位置1,如下所示:
volatile uint32_t value;
void weirdo_cpp(size_t iters, uint32_t* output) {
uint32_t x = value;
uint32_t *rdx = output;
volatile uint32_t *rsi = output;
do {
*rdx = x;
*rsi = x;
rdx += 4; // 16 byte stride
} while (--iters > 0);
}
Run Code Online (Sandbox Code Playgroud)
在汇编这个循环可能3看起来像:
weirdo_cpp:
...
align 16
.top:
mov [rdx], eax ; stride 16
mov [rsi], eax ; never changes
add rdx, 16
dec rdi
jne .top
ret
Run Code Online (Sandbox Code Playgroud)
当访问的存储区域在L2中时,我希望每次迭代运行少于3个周期.第二个商店只是一直在同一个位置,应该添加一个周期.第一个商店意味着从L2引入一条线,因此每4次迭代也会驱逐一条线.我不确定你如何评估L2成本,但即使你保守估计L1只能在每个周期中执行以下操作之一:(a)提交商店或(b)从L2接收一行或(c)将一条线驱逐到L2,对于stride-16商店流,你会得到1 + 0.25 + …
我已经了解了不同的缓存映射技术,如直接映射,关联映射和集合关联映射技术,还学习了权衡.但我很好奇现在在intel core i7或AMD处理器中使用了什么.以及这些技术是如何演变的.还有哪些事情需要改进?
在英特尔优化手册似乎对存储缓冲区的数量存在于处理器的许多地方,但谈判没有谈存储缓冲区的大小.这是公共信息还是商店缓冲区的大小保留为微架构细节?
我正在研究的处理器主要是Broadwell和Skylake,但其他人的信息也不错.
另外,存储缓冲区究竟做了什么?
一般理解为每个store分配一个store buffer entry,这个store buffer entry保存了store数据和物理地址1。
在存储跨越 4096 字节页面边界的情况下,可能需要两个不同的转换,每个页面一个,因此可能需要存储两个不同的物理地址。这是否意味着跨页存储需要 2 个存储缓冲区条目?如果是这样,它是否也适用于跨线商店?
1 ...也许还有一些/全部虚拟地址来帮助存储转发。
x86 intel cpu-architecture micro-optimization micro-architecture
Write-Combine缓冲区是如何物理连接的?我已经看到了说明许多变体的方框图:
它是依赖于微架构的吗?
我一直在使用https://github.com/google/benchmark和g++ 9.4.0来检查不同场景下数据访问的性能(用“ -O3
”编译)。结果令我惊讶。
我的基线是访问std::array
(“减少数据”)中的长数。我想添加一个额外的字节数据。一次我创建一个额外的容器(“拆分数据”),一次我在数组中存储一个结构(“组合数据”)。
这是代码:
#include <benchmark/benchmark.h>
#include <array>
#include <random>
constexpr int width = 640;
constexpr int height = 480;
std::array<std::uint64_t, width * height> containerWithReducedData;
std::array<std::uint64_t, width * height> container1WithSplitData;
std::array<std::uint8_t, width * height> container2WithSplitData;
struct CombinedData
{
std::uint64_t first;
std::uint8_t second;
};
std::array<CombinedData, width * height> containerWithCombinedData;
void fillReducedData(const benchmark::State& state)
{
// Variable is intentionally unused
static_cast<void>(state);
// Generate pseudo-random numbers (no seed, therefore always the same numbers)
// NOLINTNEXTLINE
auto engine …
Run Code Online (Sandbox Code Playgroud) c++ arrays caching compiler-optimization data-oriented-design
我需要一些帮助来了解我尝试的优化是如何工作的。
该cumsum
函数获取一个向量,并用累加和写入一个向量。
我尝试了以下方法来优化它:我没有在整个向量上执行一次循环,而是编写了一个循环,该循环在每四分之一的向量上同时运行。然后调整每个部分以考虑前面部分的总和。结果略有不同,但这不是问题。
这是程序:
module cumsum_mod
implicit none
integer, parameter, private :: dp = kind(1d0)
contains
! cumsum in one straight loop
subroutine cumsum1(n, a, b)
integer :: n, i
real(dp) :: a(n), b(n)
b(1) = a(1)
do i = 2, n
b(i) = a(i) + b(i-1)
end do
end subroutine
subroutine cumsum2(n, a, b)
integer :: n, i, m
real(dp) :: a(n), b(n)
m = n/4
! Loop over the four parts
b(1) = a(1)
b(1+m) = …
Run Code Online (Sandbox Code Playgroud) 我的输入是 2 个复数浮点向量。两个向量不交错:
VecAReal = Are0, Are1, Are2,...Are[N-1]
VecAImag = Aim0, Aim1, Aim2,...Aim[N-1]
VecBReal = Bre0, Bre1, Bre2,...Bre[N-1]
VecBImag = Bim0, Bim1, Bim2,...Bim[N-1]
Run Code Online (Sandbox Code Playgroud)
我必须运行标量乘法:
VecCReal = Cre0, Cre1, Cre2,...Cre[N-1]
VecCImag = Cim0, Cim1, Cim2,...Cim[N-1]
Cre[i] = Are[i]*Bre[i] - Aim[i]-Bim[i]
Cim[i] = Are[i]*Bim[i] + Aim[i]+Bre[i]
Run Code Online (Sandbox Code Playgroud)
在每次迭代中,我必须_mm_load_ps
在 4 个不同的指针上运行。
在 sum、add 之后,在每次迭代中我必须_mm_store_ps
在 2 个不同的指针上运行。
由于大量加载/存储,它看起来效率很低。你能建议一个更好的方法吗?
我有一个三重嵌套循环,我想并行化,但是,我遇到了数据争用问题。我很确定我需要以某种方式使用缩减,但我不太知道如何使用。
这是有问题的循环:
#pragma omp parallel for simd collapse(3)
for (uint64 u = 0; u < nu; ++u) {
for (uint64 e = 0; e < ne; ++e) {
for (uint64 v = 0; v < nv; ++v) {
uAT[u][e] += _uT[u][e][v] * wA[e][v];
}
}
}
Run Code Online (Sandbox Code Playgroud)
有人可以向我解释一下,为什么这会导致数据竞争?我真的很想了解这一点,这样我将来就不会遇到这些问题。另外,这个循环可以并行吗?如果是这样,怎么办?
编辑:我怎么知道存在数据竞争?
这个循环应该完成的任务(并且它是串行完成的)是计算不连续伽辽金框架中函数的元素平均值。当我多次运行代码时,有时会得到不同的结果,尽管它应该总是产生相同的结果。产生的错误值总是小于应有的值,这就是为什么我假设某些值没有被添加。也许这张图可以更好地解释它:第三个单元格中的平均值显然是错误的(太小)。