考虑对大量浮点数据(数百 GB)进行大规模 SIMD 矢量化循环,理论上,这些数据应该受益于非时间(“流”,即绕过缓存)加载/存储。
使用非临时存储 (_mm256_stream_ps) 实际上确实比普通存储 (_mm256_store_ps) 显着提高了约 25% 的吞吐量
但是,当使用 _mm256_stream_load 而不是 _mm256_load_ps 时,我无法测量到任何差异。
有谁有一个可以使用 _mm256_stream_load_si256 来实际提高性能的示例?
(指令集和硬件是 AMD Zen2 上的 AVX2,64 核)
for(size_t i=0; i < 1000000000/*larger than L3 cache-size*/; i+=8 )
{
#ifdef USE_STREAM_LOAD
__m256 a = _mm256_castsi256_ps (_mm256_stream_load_si256((__m256i *)source+i));
#else
__m256 a = _mm256_load_ps( source+i );
#endif
a *= a;
#ifdef USE_STREAM_STORE
_mm256_stream_ps (destination+i, a);
#else
_mm256_store_ps (destination+i, a);
#endif
}
Run Code Online (Sandbox Code Playgroud) 我尝试编写一些函数来使用单个矩阵和源向量数组来执行矩阵向量乘法。我曾经用 C++ 编写过这些函数,并在 x86 AVX512 汇编中编写过一次,以将性能与英特尔 VTune Profiler 进行比较。当使用源向量数组作为目标数组时,汇编变体的执行速度比 C++ 对应版本快 3.5 倍到 10x\xc2\xa0,但是当使用不同的源和目标数组时,汇编变体的性能几乎不比 C++ 对应版本更好,实现几乎相同的性能...有时甚至更糟。
\n我无法理解的另一件事是,为什么在使用不同的源和目标数组时,C++ 对应项甚至可以达到与汇编变体接近相同或更好的性能水平,即使汇编代码要短得多并且也根据静态分析工具 uica 和 llvm-mca 速度提高数倍。uica.uops.info
\n我不想让这篇文章变得太长,所以我只发布执行 mat4-vec4 乘法的函数的代码。
\n这是汇编变体的代码,它假设矩阵要转置:
\nalignas(64) uint32_t mat4_mul_vec4_avx512_vpermps_index[64]{ 0, 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12,\n 1, 1, 1, 1, 5, 5, 5, 5, 9, 9, 9, 9, 13, 13, 13, 13,\n 2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, 14, 14, …Run Code Online (Sandbox Code Playgroud) 为什么我们不能将数据直接从内存位置移动到另一个内存位置.
请原谅我,如果我问一个愚蠢的问题,但我认为这是一个真实的情况,至少对于我遇到的问题(8085,8086 n 80386)
我并不是在寻找一种移动数据的解决方案(例如,使用movs n all),但实际上是这种异常的原因.
我正在尝试运行从 GAS 风格获取的指令,但是当我将此指令移植到 intel 风格时,出现如下错误:
"error: parser: instruction expected"
Run Code Online (Sandbox Code Playgroud)
我尝试了各种组合,例如 REP movsl、REP loadsl,但都给出了相同的问题,任何人都可以告诉这个正确的命令相当于 x86 中的 REP stosl
在学习汇编时,我意识到我应该将经常访问的数据放在寄存器中而不是内存中,因为内存要慢得多。
问题是,由于首先要从内存中获取指令,CPU如何比内存运行得更快?CPU通常会花费大量时间等待内存中的指令吗?
编辑:要运行程序,我们需要将其编译为包含机器代码的文件。然后,我们将该文件加载到内存中,然后逐条执行一条指令。CPU需要知道要运行的指令,然后从内存中获取那条信息。我不是在问要处理数据,而是要从内存中读取指令的过程。对不起,如果我不够清楚。
编辑2:
示例:在我的计算机上xor eax, eax编译为31c0。我知道这条指令本身很快。但是要清除eax,CPU需要31c0首先从内存中读取。如果访问内存的速度很慢,那么读取操作将花费大量时间,而在此期间,CPU只是停顿了?
考虑一下 C++ 中的这个函数:
void foo(uint32_t *a1, uint32_t *a2, uint32_t *b1, uint32_t *b2, uint32_t *o) {
while (b1 != b2) {
// assert(0 <= *b1 && *b1 < a2 - a1)
*o++ = a1[*b1++];
}
}
Run Code Online (Sandbox Code Playgroud)
其目的应该足够明确。不幸的是,b1包含随机数据并垃圾缓存,成为foo我的程序的瓶颈。无论如何我可以优化它吗?
这是一个 SSCCE,应该类似于我的实际代码:
#include <iostream>
#include <chrono>
#include <algorithm>
#include <numeric>
namespace {
void foo(uint32_t *a1, uint32_t *a2, uint32_t *b1, uint32_t *b2, uint32_t *o) {
while (b1 != b2) {
// assert(0 <= *b1 && *b1 < a2 - …Run Code Online (Sandbox Code Playgroud) 在优化内循环的过程中,我遇到了奇怪的性能行为,我无法理解和纠正.
代码的精简版本如下; 粗略地说,有一个巨大的数组被分成16个字块,我简单地将每个块中字的前导零的数量加起来.(实际上我正在使用Dan Luu的popcnt代码,但是在这里我选择了一个具有类似性能特征的简单指令,用于"简洁".Dan Luu的代码基于这个SO问题的答案,虽然它具有诱人的类似奇怪的结果,似乎没有在这里回答我的问题.)
// -*- compile-command: "gcc -O3 -march=native -Wall -Wextra -std=c99 -o clz-timing clz-timing.c" -*-
#include <stdint.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#define ARRAY_LEN 16
// Return the sum of the leading zeros of each element of the ARRAY_LEN
// words starting at u.
static inline uint64_t clz_array(const uint64_t u[ARRAY_LEN]) {
uint64_t c0 = 0;
for (int i = 0; i < ARRAY_LEN; ++i) {
uint64_t t0;
__asm__ ("lzcnt %1, …Run Code Online (Sandbox Code Playgroud) 我正在检查使用最新版本的 VS 2017 C++ 编译器完成的项目的发布版本。我很好奇为什么编译器选择构建以下代码片段:
//ncbSzBuffDataUsed of type INT32
UINT8* pDst = (UINT8*)(pMXB + 1);
UINT8* pSrc = (UINT8*)pDPE;
for(size_t i = 0; i < (size_t)ncbSzBuffDataUsed; i++)
{
pDst[i] = pSrc[i];
}
Run Code Online (Sandbox Code Playgroud)
像这样:
UINT8* pDst = (UINT8*)(pMXB + 1);
UINT8* pSrc = (UINT8*)pDPE;
for(size_t i = 0; i < (size_t)ncbSzBuffDataUsed; i++)
00007FF66441251E 4C 63 C2 movsxd r8,edx
00007FF664412521 4C 2B D1 sub r10,rcx
00007FF664412524 0F 1F 40 00 nop dword ptr [rax]
00007FF664412528 0F 1F 84 00 00 00 00 …Run Code Online (Sandbox Code Playgroud) 我对STREAM(http://www.cs.virginia.edu/stream/ref.html#runrules)基准测试有一些疑问。
* (a) Each array must be at least 4 times the size of the
* available cache memory. I don't worry about the difference
* between 10^6 and 2^20, so in practice the minimum array size
* is about 3.8 times the cache size.
Run Code Online (Sandbox Code Playgroud)
例如,我添加了两个额外的数组,并确保将它们与原始a / b / c数组一起访问。我相应地修改了字节记帐。使用这两个额外的阵列,我的带宽数量增加了约11.5%。
> diff stream.c modified_stream.c
181c181,183
< c[STREAM_ARRAY_SIZE+OFFSET];
---
> c[STREAM_ARRAY_SIZE+OFFSET],
> e[STREAM_ARRAY_SIZE+OFFSET],
> d[STREAM_ARRAY_SIZE+OFFSET];
192,193c194,195
< 3 * sizeof(STREAM_TYPE) * STREAM_ARRAY_SIZE,
< 3 * sizeof(STREAM_TYPE) * …Run Code Online (Sandbox Code Playgroud) benchmarking cpu-architecture microbenchmark memory-bandwidth
这个问题让我想知道,当前的现代编译器是否曾经发出REP MOVSB/W/D指令。
基于此讨论,似乎REP MOVSB/W/D在当前 CPU 上使用可能是有益的。
但无论我如何尝试,我都无法让当前的任何编译器(GCC 8、Clang 7、MSVC 2017 和 ICC 18)发出这条指令。
对于这个简单的代码,emit 可能是合理的REP MOVSB:
void fn(char *dst, const char *src, int l) {
for (int i=0; i<l; i++) {
dst[i] = src[i];
}
}
Run Code Online (Sandbox Code Playgroud)
但是编译器会发出一个未优化的简单字节复制循环,或者一个巨大的展开循环(基本上是内联的memmove)。是否有任何编译器使用此指令?
assembly ×5
x86 ×5
c++ ×4
performance ×4
algorithm ×1
avx ×1
avx512 ×1
benchmarking ×1
c ×1
caching ×1
cpu ×1
cpu-cache ×1
hpc ×1
intel-syntax ×1
memory ×1
optimization ×1
visual-c++ ×1
x86-16 ×1
x86-64 ×1