Как быстро посчитать гистограмму входных данных ?

Тема в разделе "WASM.A&O", создана пользователем v_mirgorodsky, 29 авг 2006.

  1. v_mirgorodsky

    v_mirgorodsky New Member

    Публикаций:
    0
    Регистрация:
    7 авг 2006
    Сообщения:
    53
    Собственно $subj ;) В самом простом случае это может быть сделано следующим образом:
    Код (Text):
    1.     mov     esi,[ebx]Context.SrcBufferRdPtr
    2.     mov     ecx,[ebx]Context.SrcBufferSize
    3. BuildFreqTable:
    4.     movzx   eax,byte ptr [esi]
    5.     add     dword ptr [ebx]Context.FreqTable[4 * eax],1
    6.     add     esi,1
    7.     sub     ecx,1
    8.     jnz     BuildFreqTable
    Приведенный цикл "стоит" в районе 5 тактов на каждый входной байт. Разворачивание цикла не изменяет его производительность. То же самое с перестановкой операндов, использованием нескольких регистров и т.д.
     
  2. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    Я примерно 0,5-0,6 такта выиграл. Было 4,2 стало 3,7. Выиграш заметин только в том случае если N таково, что массив полностью поподает в кэш первого уровня.

    Код (Text):
    1.     mov        esi,offset a
    2.     mov        ecx,N/4
    3.     xor        edx,edx
    4.     mov        eax, [esi]
    5. @BuildFreqTable:
    6.     mov        dl,al
    7.     add        dword ptr [table+4 * edx],1
    8.     mov        dl,ah
    9.     add        dword ptr [table+4 * edx],1
    10.     shr        eax,16
    11.     mov        dl,al
    12.     add        dword ptr [table+4 * edx],1
    13.     mov        dl,ah
    14.     add        dword ptr [table+4 * edx],1
    15.  
    16.     add        esi,4
    17.     mov        eax, [esi]
    18.     sub        ecx,1
    19.     jnz        @BuildFreqTable
     
  3. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    А на какой входной последовательности меряем ?
    Возьмите регулярную последовательность 0, X, 2*X, 3*X ... и посмотрите, что будет при разных X.
    Например, на P4 при X=2 (+-1) простой разворот цикла на 2 дает заветные 3-3.5 тика, а вот при других X он ничего не дает - либо теже 4-6 тиков либо тормоза до 12-16 тиков. Вариант Pavia с загрузкой дворда принципиально ситуацию не меняет (при X=2 на Pentium D получается менее 3-х тиков, а при других X такие же тормоза). Вот это действительно грабли, которые в общем случае не обойдешь :dntknw:(
    А связано это видимо с конфликтом банков L1. О наличи такой проблемы в P4 Intel вроде как умалчивает, но в P6 и AMD такая проблема есть, и P4 врядли является исключением. Суть в том, что все эти процы деклалрируют возможность одновременного выполнения операций записи и чтения в L1 (dual ported cache), но на деле это возможно только в случае, когда запись и чтение направлены в разные банки. Интерлив адресации банков обычно расчитан на последовательное чтение\запись с учетом того, что реальная запись в L1 происходит только после отставки mov m,r т.е. значительно позднее ее исполнения, поэтому при последовательном копировании обычно тормозов не возникает. А в случае с гистограммой, адреса записи определяются входными данными, поэтому возможны ситуации когда очередной load попадает в тот же банк, что и давно ожидающий store, и поэтому проц задерживает load на несколько тактов пока не выполнится store (причем если входные значения идут в регулярном порядке, то и такие ситуации возникают регулярно). Такие ситуации возможны и в P6 и AMD, но в P4 эти конфликты могут усугубляться реплеем, и как с эти бороться фиг его знает - можно только расчитывать на статистику - случайный разброс входных данных, при котором конфликты банков будут возникать не часто.
    PS. Кроме конфликта банков, в P4 в принципе возможны прибамбасы со store-to-load forwarding, когда на близком расстоянии производятся чтение-запись в одну и ту же ячейку гистограммы (последующий load может опережать предыдущий store)
     
  4. v_mirgorodsky

    v_mirgorodsky New Member

    Публикаций:
    0
    Регистрация:
    7 авг 2006
    Сообщения:
    53
    Естественно, входные данные являются случайными - просто бинарный поток на сжатие. В текущей реализации счетчики являются двордами, однако переход на ворды так же ничего не дал. Да, вопрос с конфликтом по чтению/записи в L1 кэш мне в голову не приходил :dntknw: Похоже, моих 5 тактов на входной байт являются закономерным результатом в текущей реализации процессора.

    P.S. В железе гистограмму считать гораздо проще ;) В нашем случае это заняло всего один такт на входной байт ;)