我正在对科学应用进行一些数值优化.我注意到的一件事是GCC会pow(a,2)通过编译来优化调用a*a,但调用pow(a,6)没有优化,实际上会调用库函数pow,这会大大降低性能.(相比之下,英特尔C++编译器,可执行文件icc,将消除库调用pow(a,6).)
我很好奇的是,当我更换pow(a,6)与a*a*a*a*a*a使用GCC 4.5.1和选项" -O3 -lm -funroll-loops -msse4",它采用5分mulsd的说明:
movapd %xmm14, %xmm13
mulsd %xmm14, %xmm13
mulsd %xmm14, %xmm13
mulsd %xmm14, %xmm13
mulsd %xmm14, %xmm13
mulsd %xmm14, %xmm13
Run Code Online (Sandbox Code Playgroud)
如果我写(a*a*a)*(a*a*a),它会产生
movapd %xmm14, %xmm13
mulsd %xmm14, %xmm13
mulsd %xmm14, %xmm13
mulsd %xmm13, %xmm13
Run Code Online (Sandbox Code Playgroud)
这将乘法指令的数量减少到3. icc具有类似的行为.
为什么编译器不能识别这种优化技巧?
最近,我正在阅读Eric Lippert
撰写的一篇旧博客文章,其中,在写关于关联性时,他提到在C#中,(a + b) + c并不等同于a + (b + c)a,b,c的某些值.
我无法弄清楚哪些类型和范围的算术值可能是正确的以及为什么.
我是指令优化的新手.
我对一个简单的函数dotp进行了简单的分析,该函数用于获取两个浮点数组的点积.
C代码如下:
float dotp(
const float x[],
const float y[],
const short n
)
{
short i;
float suma;
suma = 0.0f;
for(i=0; i<n; i++)
{
suma += x[i] * y[i];
}
return suma;
}
Run Code Online (Sandbox Code Playgroud)
我用昂纳雾在网络上提供的测试框架testp.
在这种情况下使用的数组是对齐的:
int n = 2048;
float* z2 = (float*)_mm_malloc(sizeof(float)*n, 64);
char *mem = (char*)_mm_malloc(1<<18,4096);
char *a = mem;
char *b = a+n*sizeof(float);
char *c = b+n*sizeof(float);
float *x = (float*)a;
float *y = (float*)b;
float *z = (float*)c;
Run Code Online (Sandbox Code Playgroud)
然后我调用函数dotp,n = 2048,repeat …
我对从左到右和从右到左的相关性的定义感到困惑.我也看到他们被称为左关联和右关联,并想知道哪个对应哪个.
我知道它与执行具有相同优先级的操作的顺序有关,如a = x*y*z是指a = x*(y*z)还是a =(x*y)*z.我不知道哪一个是从左到右的关联,哪个是从右到左的关联.
我已经尝试了谷歌它,但我能找到的是不同运算符在c ++中的相关性的表格.看看所有的例子让我更加困惑.
让我更进一步困惑的是:
glm::vec4 transformedVector = translationMatrix * rotationMatrix * scaleMatrix * originalVector;
Run Code Online (Sandbox Code Playgroud)
首先预先形成缩放矩阵乘法,然后是旋转矩阵,然后是平移.在这个例子中,矩阵都是glm :: mat4类型,向量是glm :: vec4类型.这是从左到右还是从右到左的关联性?这与正常乘法相同还是glm类型的乘法不同?
为什么必须使用-ffast-mathg ++来实现使用doubles 的循环向量化?我不喜欢-ffast-math因为我不想失去精确度.
我正在尝试使用simd内部函数在C中编程矩阵乘法。我非常确定自己的实现,但是执行时,我会从所得矩阵系数的第5位开始出现一些数字错误。
REAL_T只是具有typedef的浮点数
/* This is my matmul Version with simd, using floating simple precision*/
void matmul(int n, REAL_T *A, REAL_T *B, REAL_T *C){
int i,j,k;
__m256 vA, vB, vC, vRes;
for (i=0; i<n; i++){
for (j=0; j<n; j++){
for (k=0; k<n; k= k+8){
vA = _mm256_load_ps(&A[i*n+k]);
vB = _mm256_loadu_ps(&B[k*n+j]);
vC = _mm256_mul_ps(vA, vB);
vC = _mm256_hadd_ps(vC, vC);
vC = _mm256_hadd_ps(vC, vC);
/*To get the resulting coefficient, after doing 2 hadds,
I have to get the first and the last element …Run Code Online (Sandbox Code Playgroud) 考虑下面的代码片段,显示一些简单的算术运算
int result = 0;
result = c * (a + b) + d * (a + b) + e;
Run Code Online (Sandbox Code Playgroud)
要在上面的表达式中获得结果,cpu将需要执行两次整数乘法和三次整数加法.但是在代数上,上面的表达式可以简化为下面的代码.
result = (c + d) * (a + b) + e
Run Code Online (Sandbox Code Playgroud)
这两个表达式在代数上是相同的,但第二个表达式只包含一个乘法和三个加法.gcc(或其他编译器)是否能够自己进行这种简单的优化.
现在假设编译器足够智能以进行这种简单的优化,它是否能够优化更复杂的东西,例如梯形规则(用于数值积分).下面的实施例近似下的面积sin(x),其中0 <= x <= pi与pi/4的(小为简单起见)的一个步长大小.请假设所有文字都是运行时变量.
#include <math.h>
// Please assume all literals are runtime variables. Have done it this way to
// simplify the code.
double integral = 0.5 * ((sin(0) + sin(M_PI/4) * (M_PI/4 - 0) + (sin(M_PI/4) +
sin(M_PI/2)) …Run Code Online (Sandbox Code Playgroud)