众所周知,NaNs在算术中传播,但我找不到任何演示,所以我写了一个小测试:
#include <limits>
#include <cstdio>
int main(int argc, char* argv[]) {
float qNaN = std::numeric_limits<float>::quiet_NaN();
float neg = -qNaN;
float sub1 = 6.0f - qNaN;
float sub2 = qNaN - 6.0f;
float sub3 = qNaN - qNaN;
float add1 = 6.0f + qNaN;
float add2 = qNaN + qNaN;
float div1 = 6.0f / qNaN;
float div2 = qNaN / 6.0f;
float div3 = qNaN / qNaN;
float mul1 = 6.0f * qNaN;
float mul2 = qNaN * qNaN;
printf( …Run Code Online (Sandbox Code Playgroud) 我在Windows和Linux(x86-64)上运行程序.它使用相同的编译器(Intel Parallel Studio XE 2017)编译,具有相同的选项,Windows版本比Linux版本快3倍.罪魁祸首是对std :: erf的调用,在两种情况下都在英特尔数学库中解析(默认情况下,它在Windows上动态链接,在Linux上静态链接,但在Linux上使用动态链接可以提供相同的性能).
这是一个重现问题的简单程序.
#include <cmath>
#include <cstdio>
int main() {
int n = 100000000;
float sum = 1.0f;
for (int k = 0; k < n; k++) {
sum += std::erf(sum);
}
std::printf("%7.2f\n", sum);
}
Run Code Online (Sandbox Code Playgroud)
当我使用vTune分析这个程序时,我发现Windows和Linux版本之间的程序集有点不同.这是Windows上的调用站点(循环)
Block 3:
"vmovaps xmm0, xmm6"
call 0x1400023e0 <erff>
Block 4:
inc ebx
"vaddss xmm6, xmm6, xmm0"
"cmp ebx, 0x5f5e100"
jl 0x14000103f <Block 3>
Run Code Online (Sandbox Code Playgroud)
并在Windows上调用erf函数的开头
Block 1:
push rbp
"sub rsp, 0x40"
"lea rbp, ptr [rsp+0x20]"
"lea rcx, …Run Code Online (Sandbox Code Playgroud) 在研究各种编译器的各种代码段的输出,我已经注意到,英特尔的C编译器(ICC)具有很强的偏爱发射一对趋势NEG+ ADD指令,其中其他的编译器将使用一个单一的SUB指令.
举个简单的例子,考虑以下C代码:
uint64_t Mod3(uint64_t value)
{
return (value % 3);
}
Run Code Online (Sandbox Code Playgroud)
ICC将其转换为以下机器代码(无论优化级别如何):
mov rcx, 0xaaaaaaaaaaaaaaab
mov rax, rdi
mul rcx
shr rdx, 1
lea rsi, QWORD PTR [rdx+rdx*2]
neg rsi ; \ equivalent to:
add rdi, rsi ; / sub rdi, rsi
mov rax, rdi
ret
Run Code Online (Sandbox Code Playgroud)
而其他编译器(包括MSVC,GCC和Clang)都将生成基本相同的代码,除了NEG+ ADD序列被单个SUB指令替换.
就像我说的,这不仅仅是ICC如何编写这个特定片段的怪癖.这是我在分析算术运算的反汇编时反复观察到的模式.我通常不会考虑这个,除了ICC是一个非常好的优化编译器,它是由拥有有关其微处理器的内幕信息的人开发的.
英特尔是否知道有关SUB其处理器上指令的实现的信息,将其分解为NEG+ ADD指令会更加优化?使用RISC样式的指令解码成更简单的μops是现代微体系结构的众所周知的优化建议,因此有可能SUB在内部分解为单个NEG和ADDμops,并且前端解码器使用这些实际上更有效"更简单"的指示?现代CPU很复杂,所以一切皆有可能.
我正在调查矢量化对程序性能的影响.在这方面,我写了以下代码:
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#define LEN 10000000
int main(){
struct timeval stTime, endTime;
double* a = (double*)malloc(LEN*sizeof(*a));
double* b = (double*)malloc(LEN*sizeof(*b));
double* c = (double*)malloc(LEN*sizeof(*c));
int k;
for(k = 0; k < LEN; k++){
a[k] = rand();
b[k] = rand();
}
gettimeofday(&stTime, NULL);
for(k = 0; k < LEN; k++)
c[k] = a[k] * b[k];
gettimeofday(&endTime, NULL);
FILE* fh = fopen("dump", "w");
for(k = 0; k < LEN; k++)
fprintf(fh, "c[%d] = %f\t", k, c[k]);
fclose(fh);
double …Run Code Online (Sandbox Code Playgroud) 考虑以下简单程序:
#include <cstring>
#include <cstdio>
#include <cstdlib>
void replace(char *str, size_t len) {
for (size_t i = 0; i < len; i++) {
if (str[i] == '/') {
str[i] = '_';
}
}
}
const char *global_str = "the quick brown fox jumps over the lazy dog";
int main(int argc, char **argv) {
const char *str = argc > 1 ? argv[1] : global_str;
replace(const_cast<char *>(str), std::strlen(str));
puts(str);
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
它在命令行上使用(可选)字符串并打印它,并/替换为字符_.该替换功能由c_repl功能1实现.例如, …
我对malloc与new进行了基准测试,以分配浮点数组.我的理解是malloc执行的操作是new执行的操作的一个子集 - malloc只分配但是新的分配和构造,尽管我不确定这对于原语是否有意义.
使用gcc对结果进行基准测试可以得出预期的行为.malloc()更快.甚至有些问题与此问题相反.
使用icc malloc可以比新的慢7倍.怎么可能?!
接下来的一切只是基准程序的细节.
对于基准测试,我使用了英特尔最近描述的协议.这是我的结果.
使用GNU的gcc分配4000个浮点数时,时钟周期已经过去了:
new memory allocation, cycles 12168
malloc allocation, cycles 5144
Run Code Online (Sandbox Code Playgroud)
借助英特尔的icc:
new memory allocation clock cycles 7251
malloc memory allocation clock cycles 52372
Run Code Online (Sandbox Code Playgroud)
我是如何使用malloc的:
volatile float* numbers = (float*)malloc(sizeof(float)*size);
Run Code Online (Sandbox Code Playgroud)
我是如何使用新的:
volatile float* numbers = new float[size];
Run Code Online (Sandbox Code Playgroud)
volatile是存在的,因为在之前的基准测试尝试中,我遇到了一些问题,这些编译器优化了整个函数调用并生成只存储常量的程序.(编译器选择以这种方式优化的函数实现确实比它没有的更快!)我尝试使用volatile去除只是为了确定并且结果是相同的.
我将要在两个宏之间进行基准测试的代码部分夹在中间.
功能前的宏:
#define CYCLE_COUNT_START \
asm volatile ("CPUID\n\t" \
"RDTSC\n\t" \
"mov %%edx, %0\n\t" \
"mov %%eax, %1\n\t": "=r" (cycles_high), "=r" (cycles_low):: \
"%rax", "%rbx", "%rcx", "%rdx");
Run Code Online (Sandbox Code Playgroud)
函数后面的宏:
#define CYCLE_COUNT_END \
asm volatile("RDTSCP\n\t" …Run Code Online (Sandbox Code Playgroud) 我有一系列POD结构,我试图在一个字段中求和.这是一个最小的例子:
struct Item
{
int x = 0;
int y = 0;
};
typedef Item Items[2];
struct ItemArray
{
Items items;
int sum_x1() const;
int sum_x2() const;
};
int ItemArray::sum_x1() const
{
int total = 0;
for (unsigned ii = 0; ii < 2; ++ii)
{
total += items[ii].x;
}
return total;
}
int ItemArray::sum_x2() const
{
int total = 0;
for (const Item& item : items)
{
total += item.x;
}
return total;
}
Run Code Online (Sandbox Code Playgroud)
两个sum函数做同样的事情.Clang以相同的方式编译它们.但是-O3在x86_64上的GCC 6 却没有.这是 …
我正在使用英特尔的C++编译器,它在Linux上依赖于GNU提供的libc.so和libstdc ++.所以.
这是我的问题.要访问一些最新的C++ 11功能,我需要使用随GCC 4.7或更高版本提供的libstdc ++.但是我坚持使用CentOS 6.4.
在CentOS 6.4上,GCC的原生版本是4.4.但是使用名为"SCL"的RedHat和名为"devtoolset-1.1"的软件包,我可以在"/ opt"下安装GCC 4.7.
我按照上面提到的方式设置了GCC 4.7,我可以使用更新的C++ 11功能.
所以这是我的问题:如果用户只使用GCC 4.4版本的libc.so/libstdc ++来运行我的程序,那么在库路径中,由于4.4和4.7版本之间的某些不匹配,我的程序是否存在错误风险那些图书馆?
如果存在潜在问题,我可以通过静态链接GCC 4.7的libc和libstdc ++版本来解决它吗?或者,如果/当我的代码动态加载的其他库获取系统范围的GCC 4.4软件包提供的旧的libc/libstdc ++时,是否为其他问题做好准备?
我想做这样的事情:
int main()
{
auto f = [/*some variables*/](/*take lambda function*/)
{/*something with lambda function*/};
f([/*other variables*/](/*variables to be decided by f()*/)
{/*something with variables*/});
}
Run Code Online (Sandbox Code Playgroud)
我知道可以将lambda传递给函数,也可以传递给lambda.以下作品:
int main()
{
int x=0;
int y=0;
auto f = [x,y](double (func)(int)) -> double
{func(0); return 0.0;};
f([](int i) -> double
{return 0.0;});
}
Run Code Online (Sandbox Code Playgroud)
但以下不起作用(只要我将范围变量更改为添加[x])
int main()
{
int x=0;
int y=0;
auto f = [x,y](double (func)(int)) -> double
{func(0); return 0.0;}
f([x](int i) -> double //[x] does not work
{return 0.0;});
} …Run Code Online (Sandbox Code Playgroud)