如何使用SSE(1,2,3,4)优化?

时间:2010-07-14 23:20:01

标签: c++ c optimization compiler-construction

我想知道用sse / sse2编译我的msvc项目是否会产生任何影响。我做的例如矢量标准化和点积,但我用数学做这些,而不是任何具体的功能。是否有sse_dot()和sse_normalize(),我应该用它来实现,或者编译器会知道吗?

由于

5 个答案:

答案 0 :(得分:6)

据我了解,使用sse2编译器选项将导致编译器使用标量非向量sse2指令代替普通fpu代码。我不认为它会做任何矢量化。 sse2标量的东西肯定比fpu快。

要使用向量单元,您需要直接使用内在函数(xmmintrin.h)或使用第三方库。如果你只是做简单的矢量/矩阵渲染,那么Bullet SDK就有一个优化的矢量数学库,它不错。 IIRC还对DirectX / XNAmath lib进行了优化。

如果这些都不合适,谷歌应该提出一些替代方案。

答案 1 :(得分:3)

或者你可以通过使用像Eigen,BLAS,Intel MKL这样的高性能库来明确地编写SSE内容......除非你在使用嵌入式系统,否则这些库将比你能提供的任何东西都要好得多用。

答案 2 :(得分:3)

自己编写SSE代码(asm或内在函数),使用第三方SSE优化库(例如IPP,MKL等),或使用自动矢量化编译器,如Intel的ICC。

答案 3 :(得分:1)

并非所有编译器都像您想象的那样聪明。即使是gcc也可能无法始终优化最明显的代码。请参阅以下示例并亲自尝试。 Icc似乎能够优化内循环,但gcc,因为我尝试了几个设置,不能。必要时,您必须使用SSE功能手动调用SSE / SSE2指令。人们告诉我this是一个很好的教程。

编辑:以下示例适用于Mac / Linux gcc。但它在linux上的icc失败了。我不知道为什么。 BTW,icc在矢量化方面被认为比gcc更好。

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <emmintrin.h>

float **mm_init(int n)
{
    float **m;
    int i;
    m = (float**)malloc(n * sizeof(void*));
    for (i = 0; i < n; ++i)
        m[i] = calloc(n, sizeof(float));
    return m;
}
void mm_destroy(int n, float **m)
{
    int i;
    for (i = 0; i < n; ++i) free(m[i]);
    free(m);
}
float **mm_gen(int n)
{
    float **m;
    int i, j;
    m = mm_init(n);
    for (i = 0; i < n; ++i)
        for (j = 0; j < n; ++j)
            m[i][j] = 2 * drand48() - 1.0;
    return m;
}
// better cache performance by transposing the second matrix
float **mm_mul2(int n, float *const *a, float *const *b)
{
    int i, j, k;
    float **m, **c;
    m = mm_init(n); c = mm_init(n);
    for (i = 0; i < n; ++i) // transpose
        for (j = 0; j < n; ++j)
            c[i][j] = b[j][i];
    for (i = 0; i < n; ++i) {
        float *p = a[i], *q = m[i];
        for (j = 0; j < n; ++j) {
            float t = 0.0, *r = c[j];
            for (k = 0; k < n; ++k)
                t += p[k] * r[k];
            q[j] = t;
        }
    }
    mm_destroy(n, c);
    return m;
}
// explicit SSE optimization for the inner loop
float **mm_mul3(int n, float *const *a, float *const *b)
{
    int i, j, k;
    float **m, **c, x[4];
    m = mm_init(n); c = mm_init(n);
    for (i = 0; i < n; ++i) // transpose
        for (j = 0; j < n; ++j)
            c[i][j] = b[j][i];
    for (i = 0; i < n; ++i) {
        float *p = a[i], *q = m[i];
        for (j = 0; j < n; ++j) {
            __m128 t = _mm_setzero_ps();
            float *r = c[j];
            for (k = 0; k < n; k += 4) // four operations in one CPU cycle
                t = _mm_add_ps(t, _mm_mul_ps(_mm_load_ps(p+k), _mm_load_ps(r+k)));
            _mm_store_ps(x, t);
            q[j] = x[0] + x[1] + x[2] + x[3];
        }
    }
    mm_destroy(n, c);
    return m;
}

int main(int argc, char *argv[])
{
    int n = 100;
    float **a, **b, **m;
    clock_t t;
    if (argc > 1) n = atoi(argv[1]);
    n = (n + 3) / 4 * 4; // for simplicity, n can be divided by 4
    srand48(11);
    a = mm_gen(n); b = mm_gen(n);

    t = clock();
    m = mm_mul2(n, a, b);
    fprintf(stderr, "cache:  %lf sec; M[%d][%d]=%f\n", (double)(clock() - t) / CLOCKS_PER_SEC, n/2, n/2, m[n/2][n/2]);

    t = clock();
    m = mm_mul3(n, a, b);
    fprintf(stderr, "SSE:    %lf sec; M[%d][%d]=%f\n", (double)(clock() - t) / CLOCKS_PER_SEC, n/2, n/2, m[n/2][n/2]);

    mm_destroy(n, a); mm_destroy(n, b); mm_destroy(n, m);
    return 0;
}

答案 4 :(得分:-10)

如果启用SSE2,编译器将在后面使用它。除非您打算在没有SSE2的情况下支持CPU,否则您将永远不会注意也不需要知道。这与任何其他底层CPU指令相同。