Как использовать SSE (1,2,3,4) оптимизации? - PullRequest
2 голосов
/ 15 июля 2010

Мне интересно, если простой компиляция моего проекта msvc с sse / sse2 будет иметь какой-либо эффект. Я, например, делаю векторную нормализацию и скалярное произведение, но делаю это с математикой, а не с какой-то конкретной функцией Есть ли что-то вроде sse_dot () и sse_normalize (), которые я должен использовать, чтобы использовать все преимущества, или компилятор будет знать?

Спасибо

Ответы [ 5 ]

6 голосов
/ 15 июля 2010

Насколько я понимаю, использование опции компилятора sse2 приведет к тому, что компилятор будет использовать скалярные не векторные инструкции sse2 вместо обычного кода fpu. Я не думаю, что это сделает какую-либо векторизацию. Скалярная программа sse2 наверняка быстрее fpu.

Чтобы использовать векторную единицу, вам нужно либо использовать встроенные функции напрямую (xmmintrin.h), либо использовать сторонние библиотеки, которые это делают. Если вы просто делаете простые векторные / матричные вещи для рендеринга, Bullet SDK имеет оптимизированную для sse векторную математическую библиотеку, что неплохо. IIRC библиотека DirectX / XNAmath также оптимизирована.

Если ни один из них вам не по вкусу, Google должен найти несколько вариантов.

3 голосов
/ 15 июля 2010

Либо пишите код SSE самостоятельно (asm или intrinsics), используйте сторонние библиотеки, оптимизированные для SSE (например, IPP, MKL и т. Д.), Либо используйте компилятор с автоматической векторизацией, такой как Intel ICC.

3 голосов
/ 15 июля 2010

Или вы можете избежать явного написания SSE, используя высокопроизводительную библиотеку, такую ​​как Eigen, BLAS, Intel MKL, ... Если вы не работаете со встроенной системой, эти библиотеки будут намного лучше, чем вы можете придуматьс.

1 голос
/ 15 июля 2010

Не все компиляторы настолько умны, как вы думали. Даже gcc не всегда может оптимизировать наиболее очевидный код. Посмотрите следующий пример и попробуйте сами. Icc, кажется, может оптимизировать внутренний цикл, но gcc, как я пробовал несколько настроек, не может. При необходимости вы должны вручную вызывать инструкции SSE / SSE2 с помощью функций SSE. Люди говорили мне, , это хороший учебник.

РЕДАКТИРОВАТЬ: Следующий пример работает с Mac / Linux GCC. Но это не сработает ICC на Linux. Я не знаю почему. Кстати, 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;
}
0 голосов
/ 15 июля 2010

Если вы включите SSE2, компилятор будет использовать его за вашей спиной.Вы никогда не будете замечать и не должны знать, если вы не собираетесь поддерживать процессоры без SSE2.Это то же самое, что и любая другая базовая инструкция CPU.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...