2017-01-15 16 views
1

В настоящее время я пытаюсь использовать встроенный popcnt, чтобы подсчитать количество 1 в массиве unsigned char.gcc inline asm не компилируется

У меня была функция, работающая с обычным __builtin_popcount, но с некоторыми более агрессивными требованиями к скорости, я решил пойти с inline asm.

size_t popcnt_manual(unsigned char *bytes, size_t len) { 
    size_t i, cnt = 0; 
    for(i = 0; i < len; i++) { 
     __asm__(
       "popcnt %0, %0 \n\t" 
       "add %0, %1 \n\t" 
       : "+r" (cnt) 
       : "r" (bytes[i])); 
    } 

    return cnt; 
} 

И компилятор твердит

суффикс или операнды недопустимые для добавления

ответ

2

Помимо синтаксической ошибки в коде (" "r" ->: "r"), ваша проблема несоответствующие параметры ,

Глядя на выходе из -S:

add rax, r8b 

Поскольку cnt является size_t и bytes[i] является байт, это то, что вы ожидали бы. Добавить требует, чтобы они были одного размера.

Могу ли я также использовать встроенные функции вместо встроенного asm? Он избегает таких проблем (как и многие другие).


Есть ли способ, чтобы сложить результат с POPCNT без сохранения его в регистр в первую очередь?

Umm. На самом деле это совсем другой вопрос. Ошибка, о которой вы спрашивали, связана с смешением байта и size_t в одной инструкции add. Она может быть решена, выполнив:

__asm__(
      "popcnt %0, %0 \n\t" 
      "add %0, %1 \n\t" 
      : "+r" (cnt) 
      : "r" ((size_t)bytes[i])); 

Я не призываю вас продолжать добавлять новые вопросы (? Как я могу получить мои очки кармы), но глядя на этом сайте, код кажется, он баловаться с это:

uint32_t builtin_popcnt_unrolled_errata(const uint64_t* buf, int len) { 
    assert(len % 4 == 0); 
    int cnt[4]; 
    for (int i = 0; i < 4; ++i) { 
    cnt[i] = 0; 
    } 

    for (int i = 0; i < len; i+=4) { 
    cnt[0] += __builtin_popcountll(buf[i]); 
    cnt[1] += __builtin_popcountll(buf[i+1]); 
    cnt[2] += __builtin_popcountll(buf[i+2]); 
    cnt[3] += __builtin_popcountll(buf[i+3]); 
    } 
    return cnt[0] + cnt[1] + cnt[2] + cnt[3]; 
} 

Он явно с помощью CNT [х] в попытке избежать «ложной» зависимостей проблемы POPCNT.

Использование GCC 6.1 и компиляции с -m64 -O3 -march=native -mtune=native, я вижу это как выход:

.L14: 
     popcnt r11, QWORD PTR [rcx] 
     add  rcx, 32 
     add  edx, r11d 
     popcnt r11, QWORD PTR -24[rcx] 
     add  eax, r11d 
     popcnt r11, QWORD PTR -16[rcx] 
     add  r10d, r11d 
     popcnt r11, QWORD PTR -8[rcx] 
     add  r9d, r11d 
     cmp  rcx, r8 
     jne  .L14 

Какой «хранить его в регистре» вы имеете в виду?

+0

забыл упомянуть, что я начал со встроенных функций, но хотел что-то быстрее. Есть ли способ добавить результат из popcnt, не сохраняя его сначала в регистре? Получил идею от https://danluu.com/assembly-intrinsics/ –

+0

спасибо за ответ? Я, должно быть, был просто смущен тем, что пытался сказать блог. Вот ваши точки кармы :) –

+0

В блоге нет даты, но она была написана с использованием 4.8.2. Это было давно, поэтому все могло измениться. Это еще одна причина, по которой я рекомендую использовать встроенные функции вместо встроенных. встроенные функции могут улучшиться с течением времени, но если вы напишете inline, вы получите только то, что вы пишете. BTW, если производительность критическая, использование 64-битного popcnt (вероятно) быстрее, чем повторение 8 бит. Вам нужно будет проверить len и обрабатывать нечетные размеры, но я бы попробовал и время. –