Как сделать следующий код быстрее - PullRequest
3 голосов
/ 15 декабря 2010
int u1, u2;  
unsigned long elm1[20], _mulpre[16][20], res1[40], res2[40]; 64 bits long     
res1, res2 initialized to zero.  

l = 60;  
while (l)  
{  
    for (i = 0; i < 20; i += 2)  
    {  
        u1 = (elm1[i] >> l) & 15;  
        u2 = (elm1[i + 1] >> l) & 15;

        for (k = 0; k < 20; k += 2)  
        {  
            simda = _mm_load_si128 ((__m128i *) &_mulpre[u1][k]);  
            simdb = _mm_load_si128 ((__m128i *) &res1[i + k]);  
            simdb = _mm_xor_si128  (simda, simdb);  
            _mm_store_si128 ((__m128i *)&res1[i + k], simdb);  

            simda = _mm_load_si128 ((__m128i *)&_mulpre[u2][k]);  
            simdb = _mm_load_si128 ((__m128i *)&res2[i + k]);  
            simdb = _mm_xor_si128  (simda, simdb);  
            _mm_store_si128 ((__m128i *)&res2[i + k], simdb);  
        } 
    }
    l -= 4;
    All res1, res2 values are left shifted by 4 bits.  
}

Вышеупомянутый код вызывается много раз в моей программе (профилировщик показывает 98%).

РЕДАКТИРОВАТЬ: во внутреннем цикле значения res1 [i + k] загружаются много раз для одного и того же(i + k) значения.Я попытался с этим внутри цикла while, я загрузил все значения res1 в регистры simd (массив) и использовал элементы массива внутри самого цикла for для обновления элементов массива.Как только оба цикла завершены, я сохранил значения массива обратно в res1, re2.Но время вычислений увеличивается с этим.Есть идеи, где я ошибся?Идея казалась правильной

Любое предложение сделать это быстрее можно только приветствовать.

Ответы [ 4 ]

2 голосов
/ 15 декабря 2010

К сожалению, наиболее очевидные оптимизации, вероятно, уже выполняются компилятором:

  • Вы можете получить &_mulpre[u1] и &mulpre[u2] нашего внутреннего цикла.
  • Вы можетепотяните &res1[i] нашего внутреннего цикла.
  • Использование различных переменных для двух внутренних операций и их переупорядочение может улучшить конвейеризацию.

Возможно, замена внешних цикловулучшить локальность кэша на elm1.

0 голосов
/ 23 декабря 2010
l = 60;  
while (l)  
{  
    for (i = 0; i < 20; i += 2)  
    {  
        u1 = (elm1[i] >> l) & 15;  
        u2 = (elm1[i + 1] >> l) & 15;

        for (k = 0; k < 20; k += 2)  
        {  
            _mm_stream_si128 ((__m128i *)&res1[i + k],
                    _mm_xor_si128  (
                                    _mm_load_si128 ((__m128i *) &_mulpre[u1][k]),
                                    _mm_load_si128 ((__m128i *) &res1[i + k]
                                   ));  

            mm_stream_si128 ((__m128i *)&res2[i + k],    
                    _mm_xor_si128  (
                                    _mm_load_si128 ((__m128i *)&_mulpre[u2][k]), 
                                    _mm_load_si128 ((__m128i *)&res2[i + k])
                                   ));  
        } 
    }
    l -= 4;
    All res1, res2 values are left shifted by 4 bits.  
}
  1. Помните, что вы используете встроенную функцию, используя значение _ _ 1003 * 128mi / _mm128, чтобы ускорить вашу программу.
  2. попытайтесь _mm_stream_si128 (), это может ускоритьпроцесс сохранения.
  3. попробуйте prefetch
0 голосов
/ 15 декабря 2010

Очень мало вы можете сделать с такой подпрограммой, как эта, поскольку загрузка и сохранение будут доминирующим фактором (вы выполняете 2 загрузки + 1 сохранение = 4 такта шины для одной вычислительной инструкции).

0 голосов
/ 15 декабря 2010

Ну, вы всегда можете назвать это меньше раз: -)

Общие данные ввода-вывода выглядят относительно небольшими, в зависимости от вашего дизайна и ожидаемого ввода, возможно, будет достаточно просто кэшировать вычисления или выполнять ленивую оценкувместо авансом.

...