double[]我编写了一个函数来使用 SIMD 将数组的所有元素相加(System.Numerics.Vector,性能比 na\xc3\xafve 方法差。
在我的电脑上Vector<double>.Count是 4,这意味着我可以创建一个包含 4 个值的累加器,并运行数组,按组将元素相加。
例如,一个 10 元素数组,带有 4 元素累加器和 2 个剩余元素,我会得到
\n// | loop | remainder\nacc[0] = vector[0] + vector[4] + vector[8]\nacc[1] = vector[1] + vector[5] + vector[9]\nacc[2] = vector[2] + vector[6] \nacc[3] = vector[3] + vector[7] \nRun Code Online (Sandbox Code Playgroud)\n和结果sum = acc[0]+acc[1]+acc[2]+acc[3]
下面的代码产生了正确的结果,但与仅将值相加的循环相比,速度不高
\npublic static double SumSimd(this Span<double> a)\n{\n var n = System.Numerics.Vector<double>.Count;\n var count = a.Length;\n // divide array into n=4 element groups\n …Run Code Online (Sandbox Code Playgroud) 我目前正在尝试编写一个 L1 缺失率尽可能高的程序。
为了测量 L1 缺失率,我在 Intel Core i7 处理器上使用 MEM_LOAD_RETIRED.L1_MISS 和 MEM_LOAD_RETIRED.L1_HIT 性能计数器事件(我对填充缓冲区命中不感兴趣)。我修改了 Linux 内核,以便在每次上下文切换时提供准确的测量结果,以便我可以准确地确定每个程序的命中和未命中次数。
硬件预取器被禁用。
这是我目前拥有的代码:
#define LINE_SIZE 64
#define CACHE_SIZE 4096 * 8
#define MEM_SIZE CACHE_SIZE * 64
void main(int argc, char* argv[])
{
volatile register char* addr asm ("r12") = mmap(0, MEM_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
volatile register unsigned long idx asm ("r13") = 0;
volatile register unsigned long store_val asm ("r14") = 0;
volatile register unsigned long x64 asm ("r15") = 88172645463325252ull;
while(1) …Run Code Online (Sandbox Code Playgroud) 当谈到从容器中删除多个元素时,C++ 中有一个“擦除-删除”习惯用法,并且有关于替代“调整大小-删除”方式的讨论,例如,此处。人们说“擦除删除”比“调整大小删除”更好,但根据我的测试,后者在矢量上(稍微)更快。那么,当涉及到矢量时我应该使用“调整大小删除”吗?
这是我的基准测试代码:
#include <benchmark/benchmark.h>
#include <algorithm>
#include <functional>
#include <iostream>
#include <random>
#include <vector>
using namespace std;
constexpr size_t N_ELEMS = 1000000;
constexpr int MAX_VAL = N_ELEMS / 10;
constexpr int THRESH = MAX_VAL / 5 * 3;
static vector<int> generate_input() {
vector<int> nums(N_ELEMS);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(0, N_ELEMS);
std::generate(nums.begin(), nums.end(), std::bind(dist, std::ref(gen)));
return std::move(nums);
}
static void bm_erase_remove(benchmark::State &state) {
for (auto _ : state) {
state.PauseTiming();
auto nums = generate_input();
state.ResumeTiming();
nums.erase(std::remove_if(nums.begin(), …Run Code Online (Sandbox Code Playgroud) 我正在使用"time"命令在Linux上分析程序.问题是它的输出在统计上不是很相关,因为它只运行一次程序.是否有工具或方法可以获得平均几次"时间"运行?可能与偏差等统计信息一起?
所以,我想看看我的代码中的函数需要多长时间才能运行。(实时)。最初,我有这个:
clock_t begin = clock();
my_function();
clock_t end = clock();
double time_spent = (double)(end - begin);
Run Code Online (Sandbox Code Playgroud)
但显然,这种方法存在一些问题。
那么,获取函数运行时间的正确方法是什么?CPU 时间真的是正确的方法吗?我可以测量多精确?我在想纳秒级?
我编写了两个获取数组总和的函数,第一个是用 C++ 编写的,另一个是用内联汇编 (x86-64) 编写的,我比较了这两个函数在我的设备上的性能。
如果在编译期间未启用-O标志,则使用内联汇编的函数几乎比 C++ 版本快 4-5 倍。
cpp time : 543070068 nanoseconds
cpp time : 547990578 nanoseconds
asm time : 185495494 nanoseconds
asm time : 188597476 nanoseconds
Run Code Online (Sandbox Code Playgroud)
如果-O标志设置为-O1,它们会产生相同的性能。
cpp time : 177510914 nanoseconds
cpp time : 178084988 nanoseconds
asm time : 179036546 nanoseconds
asm time : 181641378 nanoseconds
Run Code Online (Sandbox Code Playgroud)
但是,如果我尝试将-O标志设置为-O2或-O3,则使用内联汇编编写的函数会得到不寻常的2-3 位纳秒性能,该性能速度很快(至少对我来说,请耐心等待,因为我对汇编编程没有扎实的经验,所以我不知道它与用 C++ 编写的程序相比有多快或多慢。)
cpp time : 177522894 nanoseconds
cpp time : 183816275 nanoseconds …Run Code Online (Sandbox Code Playgroud)我通常把我的平等条件写成:
if(0==x)
Run Code Online (Sandbox Code Playgroud)
和许多人一样
if(x==0)
Run Code Online (Sandbox Code Playgroud)
这样编译器会告诉我什么时候不小心输入=而不是==.
有人告诉我,有些编译器将其实现为两个寄存器加载,而不是使用不等于零的操作,因此效率较低.
有人知道这是否是一个合理的评论?
我最近决定尝试一下 Java 的新孵化矢量 API,看看它能达到多快。我实现了两种相当简单的方法,一种用于解析 int,另一种用于查找字符串中字符的索引。在这两种情况下,与标量方法相比,我的矢量化方法都慢得令人难以置信。
这是我的代码:
public class SIMDParse {
private static IntVector mul = IntVector.fromArray(
IntVector.SPECIES_512,
new int[] {0, 0, 0, 0, 0, 0, 1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1},
0
);
private static byte zeroChar = (byte) '0';
private static int width = IntVector.SPECIES_512.length();
private static byte[] filler;
static {
filler = new byte[16];
for (int i = 0; i < 16; i++) {
filler[i] = zeroChar;
}
}
public static int parseInt(String str) …Run Code Online (Sandbox Code Playgroud) 我想探索 gcc (10.3) 的自动矢量化。我有以下简短的程序(请参阅https://godbolt.org/z/5v9a53aj6),它计算向量所有元素的总和:
#include <stdio.h>
#define LEN 1024
// -ffast-math -march=tigerlake -O3 -fno-unroll-loops
int
main()
{
float v[LEN] __attribute__ ((aligned(64)));
float s = 0;
for (unsigned int i = 0; i < LEN; i++) s += v[i];
printf("%g\n", s);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我用选项编译-ffast-math -march=tigerlake -O3 -fno-unroll-loops。由于 Tigerlake 处理器具有 avx512,我希望 gcc 自动向量化使用 zmm 寄存器,但它实际上在最内层循环中使用 ymm 寄存器(avx/avx2):
vaddps ymm0, ymm0, YMMWORD PTR [rax]
Run Code Online (Sandbox Code Playgroud)
如果我替换-march=tigerlake为-mavx512f,则使用 zmm 寄存器:
vaddps zmm0, zmm0, ZMMWORD PTR [rax]
Run Code Online (Sandbox Code Playgroud)
如果我只是指定,为什么不使用 …
代码行
\nnext += val;\nRun Code Online (Sandbox Code Playgroud)\n性能下降到 10 倍,我检查了 ASM 代码,而不是结果。
\n为什么这行代码会使性能下降 10 倍?
\n结果如下:
\n\xe2\x9e\x9c ~ clang-13 1.c -O3\n\xe2\x9e\x9c ~ ./a.out\nrand_read_1\nsum = 2624b18779c40, time = 0.19s\nrand_read_2\nsum = 2624b18779c40, time = 1.24s\nRun Code Online (Sandbox Code Playgroud)\nCPU:Intel(R) Xeon(R) Silver 4210 CPU @ 2.20GHz
\n这是代码:
\n#include <stdio.h>\n#include <time.h>\n#include <stdint.h>\n#include <unistd.h>\n#include <string.h>\n#include <assert.h>\n#include <stdlib.h>\n\n#define CCR_MULTIPLY_64 6364136223846793005\n#define CCR_ADD_64 1\nstatic inline uint64_t my_rand64(uint64_t *r)\n{\n *r = *r * CCR_MULTIPLY_64 + CCR_ADD_64;\n return *r;\n}\n\n#define NUM 10000000UL\n\nuint64_t rand_read_1(uint64_t *ptr, uint64_t nr_words)\n{\n uint64_t i, next, …Run Code Online (Sandbox Code Playgroud)