我一直在寻找最快的方法来处理popcount
大数据.我遇到了一个很奇怪的效果:改变从循环变量unsigned
至uint64_t
50%在我的电脑上所做的性能下降.
#include <iostream>
#include <chrono>
#include <x86intrin.h>
int main(int argc, char* argv[]) {
using namespace std;
if (argc != 2) {
cerr << "usage: array_size in MB" << endl;
return -1;
}
uint64_t size = atol(argv[1])<<20;
uint64_t* buffer = new uint64_t[size/8];
char* charbuffer = reinterpret_cast<char*>(buffer);
for (unsigned i=0; i<size; ++i)
charbuffer[i] = rand()%256;
uint64_t count,duration;
chrono::time_point<chrono::system_clock> startP,endP;
{
startP = chrono::system_clock::now();
count = 0;
for( unsigned k = 0; k < …
Run Code Online (Sandbox Code Playgroud) 我在2009年首先注意到GCC(至少在我的项目和我的机器上)如果我优化尺寸(-Os
)而不是速度(-O2
或-O3
),则会产生明显更快的代码,我一直想知道为什么.
我设法创建(相当愚蠢)代码,显示这种令人惊讶的行为,并且足够小,无法在此处发布.
const int LOOP_BOUND = 200000000;
__attribute__((noinline))
static int add(const int& x, const int& y) {
return x + y;
}
__attribute__((noinline))
static int work(int xval, int yval) {
int sum(0);
for (int i=0; i<LOOP_BOUND; ++i) {
int x(xval+sum);
int y(yval+sum);
int z = add(x, y);
sum += z;
}
return sum;
}
int main(int , char* argv[]) {
int result = work(*argv[1], *argv[2]);
return result;
}
Run Code Online (Sandbox Code Playgroud)
如果我用-Os
它编译它,执行这个程序需要0.38秒,如果用-O2 …
我正在阅读Agner Fog的优化手册,并且遇到了这个例子:
double data[LEN];
void compute()
{
const double A = 1.1, B = 2.2, C = 3.3;
int i;
for(i=0; i<LEN; i++) {
data[i] = A*i*i + B*i + C;
}
}
Run Code Online (Sandbox Code Playgroud)
Agner 指出,有一种方法可以优化此代码 - 通过认识到循环可以避免使用昂贵的乘法,而是使用每次迭代应用的“增量”。
我用一张纸来证实这个理论,首先......
...当然,他是对的 - 在每次循环迭代中,我们可以通过添加“增量”,基于旧结果计算新结果。该增量从值“A+B”开始,然后每一步增加“2*A”。
所以我们将代码更新为如下所示:
void compute()
{
const double A = 1.1, B = 2.2, C = 3.3;
const double A2 = A+A;
double Z = A+B;
double Y = C;
int i;
for(i=0; i<LEN; i++) {
data[i] …
Run Code Online (Sandbox Code Playgroud) 我发现了这个流行的大约 9 岁的SO 问题,并决定仔细检查其结果。
所以,我有 AMD Ryzen 9 5950X、clang++ 10 和 Linux,我从问题中复制粘贴了代码,这是我得到的:
排序 - 0.549702s:
~/d/so_sorting_faster$ cat main.cpp | grep "std::sort" && clang++ -O3 main.cpp && ./a.out
std::sort(data, data + arraySize);
0.549702
sum = 314931600000
Run Code Online (Sandbox Code Playgroud)
未分类 - 0.546554s:
~/d/so_sorting_faster $ cat main.cpp | grep "std::sort" && clang++ -O3 main.cpp && ./a.out
// std::sort(data, data + arraySize);
0.546554
sum = 314931600000
Run Code Online (Sandbox Code Playgroud)
我很确定 unsorted 版本比 3ms 快的事实只是噪音,但它似乎不再慢了。
那么,CPU 的架构发生了什么变化(使其不再慢一个数量级)?
以下是多次运行的结果:
Unsorted: 0.543557 0.551147 0.541722 0.555599
Sorted: …
Run Code Online (Sandbox Code Playgroud) long long int n = 2000*2000*2000*2000; // overflow
long long int n = pow(2000,4); // works
long long int n = 16000000000000; // works
Run Code Online (Sandbox Code Playgroud)
为什么第一个溢出(乘以整数文字常量以分配给 long long)?
它与第二个或第三个有什么不同?
我在解决代码问题上遇到了一些问题.通常我首先检查字符是英文字母的上部还是下部,然后减去或添加32
以将其转换为相应的字母.但我发现有人^= 32
做了同样的事情.这里是:
char foo = 'a';
foo ^= 32;
char bar = 'A';
bar ^= 32;
cout << foo << ' ' << bar << '\n'; // foo is A, and bar is a
Run Code Online (Sandbox Code Playgroud)
我已经搜索了这方面的解释并没有找到答案.那么为什么会这样呢?
陷阱和中断有什么区别?
如果不同系统的术语不同,那么它们在x86上意味着什么?
以下链接解释了UNIX(BSD风格)和Linux的x86-32系统调用约定:
但是UNIX和Linux上的x86-64系统调用约定是什么?
我相信我在实现 O'Neill 的 PCG PRNG 时在 GCC 中发现了一个错误。(Godbolt 编译器资源管理器上的初始代码)
相乘后oldstate
通过MULTIPLIER
,(存储在RDI结果),GCC不该结果添加到INCREMENT
,movabs'ingINCREMENT
到RDX代替,然后把它用作rand32_ret.state的返回值
最小可重现示例(编译器资源管理器):
#include <stdint.h>
struct retstruct {
uint32_t a;
uint64_t b;
};
struct retstruct fn(uint64_t input)
{
struct retstruct ret;
ret.a = 0;
ret.b = input * 11111111111 + 111111111111;
return ret;
}
Run Code Online (Sandbox Code Playgroud)
生成的程序集(GCC 9.2、x86_64、-O3):
fn:
movabs rdx, 11111111111 # multiplier constant (doesn't fit in imm32)
xor eax, eax # ret.a = 0
imul rdi, rdx
movabs rdx, 111111111111 …
Run Code Online (Sandbox Code Playgroud)