我一直有这个想法,减少迭代次数是的方式来使程序更加高效.由于我从未真正确认过,我开始测试这个.
我制作了以下C++程序来测量两个不同函数的时间:
完整的测试代码:
#include <iostream>
#include <chrono>
using namespace std;
int* list1; int* list2;
int* list3; int* list4;
int* list5; int* list6;
int* list7; int* list8;
int* list9; int* list10;
const int n = 1e7;
// **************************************
void myFunc1()
{
for (int i = 0; i < n; i++)
{
list1[i] = 2;
list2[i] = 4;
list3[i] = 8;
list4[i] = 16;
list5[i] = 32;
list6[i] = 64;
list7[i] = 128;
list8[i] = 256;
list9[i] = …Run Code Online (Sandbox Code Playgroud) 我正在阅读有关写入组合内存的英特尔优化手册并编写了基准测试以了解其工作原理。这些是我正在运行基准测试的 2 个函数:
memcopy.h:
void avx_ntcopy_cache_line(void *dest, const void *src);
void avx_ntcopy_64_two_cache_lines(void *dest, const void *src);
Run Code Online (Sandbox Code Playgroud)
memcopy.S:
avx_ntcopy_cache_line:
vmovdqa ymm0, [rdi]
vmovdqa ymm1, [rdi + 0x20]
vmovntdq [rsi], ymm0
vmovntdq [rsi + 0x20], ymm1
;intentionally no sfence after nt-store
ret
avx_ntcopy_64_two_cache_lines:
vmovdqa ymm0, [rdi]
vmovdqa ymm1, [rdi + 0x40]
vmovntdq [rsi], ymm0
vmovntdq [rsi + 0x40], ymm1
;intentionally no sfence after nt-store
ret
Run Code Online (Sandbox Code Playgroud)
这是基准测试的主要功能的样子:
#include <stdlib.h>
#include <inttypes.h>
#include <x86intrin.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include "memcopy.h" …Run Code Online (Sandbox Code Playgroud) 共享内存多处理系统通常需要为缓存一致性生成大量流量。核心 A 写入缓存。Core B 稍后可能会读取相同的内存位置。因此,内核 A,即使它本来可以避免写入主内存,也需要向内核 B 发送通知,告诉 B 如果该地址正在缓存中,则该地址无效。
究竟什么时候需要这样做,这是一个复杂的问题。不同的 CPU 架构有不同的内存模型,这里上下文中的内存模型是一组关于观察到的事情发生的顺序的保证。内存模型越弱,A 在发送通知的确切时间就越放松对于 B,A 和 B 更容易并行做更多的事情。不同 CPU 架构的内存模型总结:https : //en.wikipedia.org/wiki/Memory_ordering#Runtime_memory_ordering
所有的讨论似乎是关于当失效发生时,什么为了事情发生英寸
但在我看来,在许多工作负载中,A 写入的大部分数据永远不会被 B 使用,因此如果可以完全消除那些缓存失效的总线流量会更好。专用于执行缓存一致性的硬件仍然需要存在,因为 A 和 B 有时需要共享数据,但写入共享总线是 CPU 可以做的更耗能的事情之一,并且电池寿命和散热通常是现在限制资源,因此减少总线流量将是一个有用的优化。有没有办法做到这一点?
从效率的角度来看,理想的情况是如果忽略总线流量是默认的(因为大多数写入的数据不与其他线程共享),并且您必须在需要缓存一致性的地方显式地发出内存屏障。另一方面,这可能是不可能的,因为假设它在 x86 或 ARM 上运行的现有代码量很大;有没有办法反过来,向 CPU 指示给定的缓存行永远不会对任何其他线程感兴趣?
我会对任何系统的答案感兴趣,但最特别是 x64、ARM 或 RISC-V 上 Linux 最常见的当前/未来服务器配置。
multithreading cpu-architecture memory-model memory-barriers cpu-cache
请考虑以下最小示例minimal.cpp(https://godbolt.org/z/x7dYes91M)。
#include <immintrin.h>
#include <algorithm>
#include <ctime>
#include <iostream>
#include <numeric>
#include <vector>
#define NUMBER_OF_TUPLES 134'217'728UL
void transform(std::vector<int64_t>* input, std::vector<double>* output, size_t batch_size) {
for (size_t startOfBatch = 0; startOfBatch < NUMBER_OF_TUPLES; startOfBatch += batch_size) {
size_t endOfBatch = std::min(startOfBatch + batch_size, NUMBER_OF_TUPLES);
for (size_t idx = startOfBatch; idx < endOfBatch;) {
if (endOfBatch - idx >= 8) {
auto _loaded = _mm512_loadu_epi64(&(*input)[idx]);
auto _converted = _mm512_cvtepu64_pd(_loaded);
_mm512_storeu_epi64(&(*output)[idx], _converted);
idx += 8;
} else {
(*output)[idx] …Run Code Online (Sandbox Code Playgroud) 某些CPU和编译器提供预取指令.例如:GCC文档中的 __builtin_prefetch .虽然GCC的文件中有评论,但它对我来说太短了.
我想知道,在prantice中,我们应该何时使用预取?有一些例子吗?谢谢!
我测试了以下简单的功能
void mul(double *a, double *b) {
for (int i = 0; i<N; i++) a[i] *= b[i];
}
Run Code Online (Sandbox Code Playgroud)
具有非常大的数组,因此它受内存带宽限制.我使用的测试代码如下.当我用-O2它编译它需要1.7秒.当我用-O2 -mavx它编译它只需要1.0秒.非vex编码的标量操作慢了70%!为什么是这样?
系统:i7-6700HQ@2.60GHz(Skylake)32 GB内存,Ubuntu 16.10,GCC 6.3
测试代码
//gcc -O2 -fopenmp test.c
//or
//gcc -O2 -mavx -fopenmp test.c
#include <string.h>
#include <stdio.h>
#include <x86intrin.h>
#include <omp.h>
#define N 1000000
#define R 1000
void mul(double *a, double *b) {
for (int i = 0; i<N; i++) a[i] …Run Code Online (Sandbox Code Playgroud) 我写了一个简单的测试(代码在底部)来memcpy对我的 64 位 Debian 系统的性能进行基准测试。在我的系统上编译为 64 位二进制文件时,这在所有块大小上提供了一致的 38-40GB/s。然而,当在同一系统上构建为 32 位二进制文件时,复制性能非常糟糕。
我在汇编程序中编写了自己的 memcpy 实现,该实现利用了能够匹配 64 位性能的 SIMD。老实说,我自己的 memcpy 比原生的快得多,这让我感到震惊,当然 32 位 libc 构建肯定有问题。
0x00100000 B, 0.034215 ms, 29227.06 MB/s (16384 iterations)
0x00200000 B, 0.033453 ms, 29892.56 MB/s ( 8192 iterations)
0x00300000 B, 0.048710 ms, 20529.48 MB/s ( 5461 iterations)
0x00400000 B, 0.049187 ms, 20330.54 MB/s ( 4096 iterations)
0x00500000 B, 0.058945 ms, 16965.01 MB/s ( 3276 iterations)
0x00600000 B, 0.060735 ms, 16465.01 MB/s ( 2730 iterations)
0x00700000 B, …Run Code Online (Sandbox Code Playgroud) 我试图用rep movsb指令创建我的 memcpy 代码。当优化被禁用时,它适用于任何尺寸。但是,当我启用优化时,它没有按预期工作。
我从Intel® 64 and IA-32 Architectures Optimization Reference Manual section 3.7.6 中阅读了有关 memcpy 的增强 movsb 。我来到了 libc 源代码,我看到 libc 的默认 memcpy 使用 SSE 而不是movsb.
因此,我想比较memcpy 的SSE 指令和rep movsb之间的性能。但是现在,我发现它有些不对劲。
#include <stdio.h>
#include <string.h>
inline static void *my_memcpy(
register void *dest,
register const void *src,
register size_t n
) {
__asm__ volatile(
"mov %0, %%rdi;"
"mov %1, …Run Code Online (Sandbox Code Playgroud) 我得到了错误的结果,因为我在测量时没有包括这里讨论的预取触发事件。话虽如此,AFAIKrep movsb与临时存储相比,我只看到 RFO 请求减少,memcpy因为在加载时预取更好,而没有对存储进行预取。不是因为 RFO 请求针对完整缓存行存储进行了优化。这种有意义的,因为我们没有看到RFO请求优化掉了vmovdqa一个zmm寄存器,我们预计如果真的在那里为整个缓存线存储情况。话虽如此,存储上缺乏预取和非临时写入的缺乏使得很难看出如何rep movsb具有合理的性能。
编辑:RFO 可能来自rep movsb不同的请求vmovdqa,因为rep movsb它可能不请求数据,只需在独占状态下取行即可。对于有收银机的商店,情况也可能如此zmm。但是,我没有看到任何性能指标来测试这一点。有谁知道吗?
rep movsb的memcpy作为相比,memcpy与实现的vmovdqa?rep movsb的memcpy作为相比,memcpy与实现vmovdqa两个单独的问题,因为我相信我应该看到 RFO 请求减少了rep movsb,但如果不是这种情况,我是否也应该看到增加?
CPU - Icelake: Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz
我试图在使用不同的方法时测试 RFO 请求的数量,memcpy包括:
vmovdqa为了测量主存储器的带宽,我提出了以下方法。
代码(针对英特尔编译器)
#include <omp.h>
#include <iostream> // std::cout
#include <limits> // std::numeric_limits
#include <cstdlib> // std::free
#include <unistd.h> // sysconf
#include <stdlib.h> // posix_memalign
#include <random> // std::mt19937
int main()
{
// test-parameters
const auto size = std::size_t{150 * 1024 * 1024} / sizeof(double);
const auto experiment_count = std::size_t{500};
//+/////////////////
// access a data-point 'on a whim'
//+/////////////////
// warm-up
for (auto counter = std::size_t{}; counter < experiment_count / 2; ++counter)
{
// garbage data allocation and memory page loading …Run Code Online (Sandbox Code Playgroud) c++ benchmarking assembly performance-testing memory-bandwidth