Почему операционная система в этой встроенной сборке работает неправильно? - PullRequest
2 голосов
/ 09 июня 2011

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

Я бы заменил это:

void swapTwoByte(char* array, int numChunks)
{
    for(int i= (2*numChunks-1); i>=0; i-=2)
    {
        char temp=array[i];
        array[i]=array[i-1];
        array[i-1]=temp;
    }
}

на это:

void swapTwoByte(int16* array, int numChunks)
{
    for(int i= (numChunks-1); i>=0; --i)
    {
        asm("movw %1, %%ax;"
            "rorw %%ax;"
            "rorw %%ax;"
            "rorw %%ax;"
            "rorw %%ax;"
            "rorw %%ax;"
            "rorw %%ax;"
            "rorw %%ax;"
            "rorw %%ax;"
            "movw %%ax, %0;"
            : "=r" ( array[i] )
            : "r" (array[i])
            :"%ax"
        );
    }
}

Что выполняет намеченную работу,но это много операций поворота.

Итак, вот мой вопрос: согласно этот источник rorw может принимать два операнда, а в синтаксисе газа исходный операнд должен быть числомбиты для поворота, но каждый раз, когда я пытаюсь заменить этот список из 8 прав поворота на что-то вроде

".set rotate, 0x0008"
"rorw rotate, %%ax"

, я получаю сообщение об ошибке ассемблера:

"Error: number of operands mismatch for `ror'"

Почему это так?Чего мне не хватает?

Ответы [ 3 ]

10 голосов
/ 09 июня 2011

Прежде всего, используйте

#include <arpa/inet.h>
little_endian = ntohs(big_endian);

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

Однако это не решит проблему с производительностью, так как я считаю, что вы ее неправильно определили.Первое правило микрооптимизации Nemo: «Математика - это быстро; память - медленно».

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

Так что не меняйте местами байты, пока вы не используете их .Мой личный любимый подход заключается в следующем:

class be_uint16_t {
public:
        be_uint16_t() : be_val_(0) {
        }
        be_uint16_t(const uint16_t &val) : be_val_(htons(val)) {
        }
        operator uint16_t() const {
                return ntohs(be_val_);
        }
private:
        uint16_t be_val_;
} __attribute__((packed));

Это определяет двухбайтовый класс, который представляет число с прямым порядком байтов в памяти.Он неявно приводится к и из uint16_t по мере необходимости.Так что приведите указатель вашей памяти к be_uint16 * и просто обращайтесь к нему как к массиву;забудьте о замене байтов, потому что класс сделает это за вас:

const be_uint16_t *p = (be_uint16 *)my_block;
unsigned val = p[37];  // or whatever

Обратите внимание, что вы можете даже делать такие вещи:

be_uint16_t x = 12;
x = x + 1;
write(fd, &x, sizeof(x)); // writes 13 to file in big-endian form

Затраты на замену значения непосредственно передиспользование, по моему опыту, необнаружимо.Местность - это название игры ...

5 голосов
/ 09 июня 2011

Подумайте немного реорганизовать этот код C ++.Как написано, g ++ 4.5.2 компилирует его для меня как скучный жесткий цикл с четырьмя 8-битными mov с и двумя приращениями указателей.

.L3:
    movzbl  (%rdi), %eax
    movzbl  -1(%rdi), %edx
    movb    %al, -1(%rdi)
    movb    %dl, (%rdi)
    subq    $2, %rdi
    subl    $2, %esi
    jns .L3

, переписывая его как

void swapTwoByte(char* array, int numChunks)
{
    for(int i = 0; i<numChunks*2; i+=2)
        std::swap(array[i], array[i+1]);
}

позволяет компилятору понять, что вы делаете, и включить полную SIMD-мощность, теперь цикл ядра обрабатывает 32 байта за раз:

.L4:
    movdqu  (%rdx), %xmm1
    movdqu  (%rax), %xmm2
    movdqa  %xmm1, %xmm0
    movdqa  %xmm2, %xmm3
    pshufb  %xmm7, %xmm0
    pshufb  %xmm4, %xmm2
    pshufb  %xmm6, %xmm3
    pshufb  %xmm5, %xmm1
    por %xmm3, %xmm0
    por %xmm2, %xmm1
    incl    %ecx
    movdqa  %xmm1, %xmm2
    punpckhbw   %xmm0, %xmm1
    punpcklbw   %xmm0, %xmm2
    movdqu  %xmm2, (%rdx)
    movdqu  %xmm1, (%rax)
    addq    $32, %rdx
    addq    $32, %rax
    cmpl    %ecx, %r8d
    ja  .L4

a rorw не превзойдет это.

0 голосов
/ 09 июня 2011

Инструкция подкачки работает с 32-битными значениями.Чтобы поменять местами два байта, используйте инструкцию xchg al, ah .

...