Влияние команд чтения-записи на производительность (+цикл +SSE)

Тема в разделе "WASM.A&O", создана пользователем ettaine, 28 янв 2010.

  1. ettaine

    ettaine New Member

    Публикаций:
    0
    Регистрация:
    28 янв 2010
    Сообщения:
    7
    Удаление всей арифметики на загрузку не влияет вообще :))). Независимо от длины массивов.
    Изменяла длину больших массивов (src, res).
    Оставила от цикла следующее:
    Вариант А
    Код (Text):
    1. start:
    2.         movaps xmm0, [eax]         
    3.         movaps xmm1, [ebx]         
    4.         add eax,16                 
    5.         add ebx,16
    6.         movaps [edi],xmm0
    7.  
    8.         add edi,16
    9.         cmp ebx,edx
    10.         jb start
    11.        
    12.         mov ebx, ptr_in
    13.         cmp edi,limit
    14.         cmovae edi, begin_res
    15.         cmp edi,res
    16.         jne start
    Вариант Б
    Код (Text):
    1. start:
    2.         add ebx,16
    3.         movaps [edi],xmm0
    4.  
    5.         add edi,16
    6.         cmp ebx,edx
    7.         jb start
    8.        
    9.         mov ebx, ptr_in
    10.         cmp edi,limit
    11.         cmovae edi, begin_res
    12.         cmp edi,res
    13.         jne start
    Загрузка в процентах следующая:
    длина массива А Б
    32768 2 1...2
    65536 4 2
    200000 11..12 7
    400000 22..23 15...16
    С арифметикой результат как для варианта А (плюс-минус полпроцента)
    При комментировании только этих команд
    Код (Text):
    1.         movaps xmm0, [eax]         
    2.         movaps xmm1, [ebx]
    при длине 400000 загрузка 21-22
    а только этих
    Код (Text):
    1.         addps xmm0,[edi]
    2.         movaps [edi],xmm0
    при длине 400000 загрузка 6-7

    Длину меньшего массива ставила 1024, 2048, 4096. Загрузка уменьшается с увеличением длины. Например, для 400000:
    1024: 22...23
    2048: 11
    4096: 6
    В случае с изменением длины длинных массивов вроде понятно: чем длиннее - тем больше операций.
    Для короткого массива - я в недоумении, т.к. изменение его длины не должно влиять на к-во операций (длинный массив делится на куски, равные длине короткого). Т.е. уменьшается к-во циклов. С этой точки зрения получается, что во всем виноват вот этот фрагмент:
    Код (Text):
    1. cmp ebx,edx
    2.     jb start
    но это вроде не совсем согласуется с тем что выше... (еще перепроверю потом)

    Да, это проект в Visual Studio.
    Память массивам раздаю через aligned_malloc и некоторым через new (где лень мешала поисправлять на aligned_malloc).
    В memset не думаю что есть необходимость - массив все равно обрабатывается периодически один и тот же... Начальный всплеск загрузки иногда есть, но я его не учитываю.
    Кстати. Эта функция вызывается несколько раз, причем короткий массив один и тот же, а длинные каждый раз другие...

    О да, это она :)

    Эх... Слепить что ли длл в матлабе и использовать в своем коде? :) 8-)
     
  2. ConstZ

    ConstZ New Member

    Публикаций:
    0
    Регистрация:
    18 фев 2008
    Сообщения:
    42
    2 ettaine

    Посмотрите здесь: есть интересные моменты по оптимизации SSE.
     
  3. persicum

    persicum New Member

    Публикаций:
    0
    Регистрация:
    2 фев 2007
    Сообщения:
    947
    ettaine
    А разве поэлементное умножение не составляет всего лишь несколько процентов трудоемкости от общей задачи свертки, которая требует два-три FFT, прогоняющие вектора log n раз?
     
  4. persicum

    persicum New Member

    Публикаций:
    0
    Регистрация:
    2 фев 2007
    Сообщения:
    947
    ConstZ
    Спасибо за ссылку! почитал и почти все из этого интуитивно применял, что касается группировки. Только вот лезть в память несколько раз вместо копирования про запас в регистр на это рука никогда не подымится...

    А где можно почитать или услышать рекомендации про
    1) смешение SSE2 с MMX, в частности, что делать с emms, куда ее притулить?
    2) SSE2 и многоядерность, может ли быть в камне два ALU и один FPU
     
  5. PSR1257

    PSR1257 New Member

    Публикаций:
    0
    Регистрация:
    30 ноя 2008
    Сообщения:
    933
    ettaine

    Спасибо за проведенный опыт. Вроде бы как получается что основная загрузка идет за счет записи в память (movaps [edi],xmmN) и есть четкая зависимость между размером массива-приемника (sic!) - ?

    Вроде бы как получается при достижении всей памяти какого-то значения (размер кэша? Он вроде ~512KБайт) происходит существенный прирост:

    65536 4 2
    200000 11..12 7

    Если это так (можно уточнить экспериментом с просто записью в _один_ массив с минимально возможным числом команд), то, видимо, нужно стремиться к этим (т.е. которые для размера массива <=65536) значениям - дальнейшая оптимизация просто невозможна.

    Это был быстрый ответ, надо подумать еще... Я немного посмотрел статью по ссылке ConstZ - там вроде бы про оптимизацию записи нет ничего... По идее можно удвоить число элементов (если точность устраивает) используя 64-х битные элементы вместо 128-ми битных или же как-то реорганизовать данные чтобы одномоментно размер текущих массивов умещался в кэш (видимо часть его, так как есть еще и другие процессы).
     
  6. LiveM

    LiveM New Member

    Публикаций:
    0
    Регистрация:
    30 янв 2008
    Сообщения:
    4
    ettaine
    Попробуйте писать результаты в отдельный массив.

    Код (Text):
    1. addps xmm0,[edi]
    2. movaps [edi],xmm0
    - нехорошая идея, потому, что
    "Постоянно" обновляется линейка кэша, идет то запись, то чтение. Предвыборка от этого сильно страдает и вылезает латентнось доступа к памяти.
    Предположим, все данные в L2, тогда время выполнения этих команд будет складыватся из:
    addps xmm0,[edi] - загрузки данных из L2 + latency сложения
    movaps [edi],xmm0 - тут (если не ошибаюсь у Intel-a запись идет в L2) идет запись в L2 линейки кэша (64b)
    на следующей итерации:
    addps xmm0,[edi] - читаем 16b из той же линейки, придется ждать когда она обновится и потом еще прочитается... Возможно я несовсем прав и кэш контроллер пытается это как-то разрулить, но ему явно плохо.
    Если же [edi] вылезает из L2, все становится еще хуже.
    Если вам необходимо чтобы результаты писались в тот же массив, пишите небольшими блоками во временный, а потом в исходный.

    Кстати, посмотрите _http://download.intel.com/technology/itj/2004/volume08issue01/art02_compilers/vol8iss1_art02.pdf может, это то что вам надо.

    Измерение ресурсоемкости функции в % что-то говорит только вам, в дальнейшем пишите хотя бы время выполнения N вызовов ф-ии в тактах и модель процессора.

    Кроме Intel Manuals, рекомендую почитать Агнера Фога - agner.org и "Техника оптимизации программ.." Крис Касперски(немного устарела но многое разжевано и на русском).
     
  7. LiveM

    LiveM New Member

    Публикаций:
    0
    Регистрация:
    30 янв 2008
    Сообщения:
    4
    1) emms в конце ф-ии. А зачем вам мешать mmx и sse? (такое бывает очень редко)
    2) Что вы понимаете под камнем? если ядро, то в каждом ядре современного x86 процессора несколько исполнительных устройств, относящихся как к ALU и FPU. Многоядерные процессоры исполнительные блоки не разделят. А вот с HT как раз раз делят.

    про внутренне устройство можно прочитать в "Intel® 64 and IA-32 Architectures Optimization Reference Manual"
     
  8. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    ettaine
    Ты бы для начала со своим алгоритмом разобралась, а то в твоих отрывочных сумбурных мыслях вслух - одни нестыковки ;)

    Во-первых, говоришь у тебя буфер res - кольцевой, но размерчики 100000 и т.д. почему-то постоянно указываешь некратные размеру входного ptr_in. Как у тебя limit устанавливается не понятно, поэтому в результае можно предположить, что ты просто выскакиваешь за пределы буфера res при cmp + cmovae
    Во-вторых, проверки длины массива src в цикле нет, из чего видимо следует, что размеры src и res равны, но тут смущает отрывочная фраза "первых два массива влазят в кэш, третий нет"
    В-третьих, с одной стороны говоришь, что позиция res "при каждом вызове смещается на размер ptr_in", а с другой стороны "функция вызывается несколько раз, причем короткий массив один и тот же, а длинные каждый раз другие...". Как это понимать ?

    И цифирьки у тебя весьма странные получаются. "Для короткого массива - я в недоумении" - это вобще явный признак косяка в алгоритме. Для длинных массивов тоже "интересный" результат - практически пропорциональный рост с размером массива, хотя пара массивов по 32тыс. явно должна умещаться в L2, а по 400тыс. явно нет и поэтому, по крайней мере, должны возникать доп.тормоза на сброс измененного массива res в ОЗУ, а это как мин. 30% на доп.поток обмена данными с ОЗУ. Это при условии, что при каждом вызове массивы другие и соотв-но при любом размере грузятся из ОЗУ, а если бы они использовались повторно, то доп.разница между 32 и 400тыс. достигала бы десятка и более раз. В итоге вся эта пропорциональность говорит либо о наличии каких-то больших доп.тормозов, либо о том что "загрузка процессора" измеряется как-то не так...

    PS: Большие тормоза могут быть при использовании неинициализированных массивов, выделяемых через VirtualAlloc. В винде и в MSVC все массивы размером более ~512К выделяются VirtualAlloc'ом и соотв-но при первом обращении к каждой 4К странице возникает page fault для ее инициализации. Хотя массивы в 32тыс. это всего 128К и должны выделяться из общей кучи, тем не менее если их каждый раз освобождать и выделять заново, то виндовый менеджер может делать decommit страниц при освобождении (все таки 256К это не мелочь) и повторный commit при выделении
     
  9. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    LiveM
    Ес-но. Во-первых, запись производится не сразу в кэш, а сначала в store-буферы и только спустя N-е кол-во тактов (порядка длины конвеера) данные сбрасываются из буферов в кэш. Это делается для того, чтобы при спекулятивном исполнении ветвлений и изменении порядка команд гарантированно дождаться подтверждения и отставки всех команд, предшествоваших записи. Во-вторых, для того чтобы можно было одновременно читать из кэша и записывать в него, его делают состоящим из нескольких "банков" данных, причем таким образом, чтобы соседние dword или qword-ы каждой линейки кэша принадлежали разным банкам. Поэтому конфликт между чтением и записью возникает только либо при чтении\записи по одному и тому же адресу (в одну переменную), либо по адресам, различающимся на размер линейки кэша. Конечно, с учетом того, что реальная запись в кэш идет с задержкой, все эти конфликты трудно предугадать. Но ясно, что 1) чем меньше разных массивов используется при обработке, тем лучше и 2) желательно адреса одновременно обрабатываемых массивов выравнивать на размер линейки (64 байта), тогда, например, для рассматриваемого случая чтение данных из всех трех массивов будет каждый раз производиться из одного банка кэша и соотв-но вероятность того, что запись будет именно в этот банк - меньше (чем если бы при чтении юзались 3 разных банка)
     
  10. LiveM

    LiveM New Member

    Публикаций:
    0
    Регистрация:
    30 янв 2008
    Сообщения:
    4
    leo
    Спасибо за коментарий!
    Про буфера записи и ассоциативность кэша представление имею, но действительно, в данном случае конфликт не возникает, это я словил интересный "глюк" и ошибочно принял его за конфликт кэша. Сейчас пытаюсь разобраться.

    не всегда. например, в данном случае будет конфликт
    Код (Text):
    1.  addps     xmm0,xmmword ptr [eax]
    2.  mov       dword ptr [eax], reg
    так же
    Intel® 64 and IA-32 Architectures Optimization Reference Manual. Chapter 7.2 про HARDWARE PREFETCHING
    По п2.
    противоречит
    A Fog по этому поводу пишет
    Хотя к первоначальному вопросу это пожалуй не относится, но все же считаю что в большинстве случаев читать и писать лучше в разные массивы. Прошу поправить, если ошибаюсь.

    p.s. все что выше, писал относительно Core2
     
  11. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    LiveM
    С какой стати ?! Не будет тут никакого конфликта

    Это вообще тут причем ? Или ты думаешь для load свой префетч, а для store - свой :) Префетч работает с линейками кэша и ему пофиг читаешь ты или пишешь, т.к. если ты даже только записываешь, он обязан сначала загрузить (прочитать) линейку из памяти или вышестоящего кэша и только потом в нее записать.
    Поэтому твое предложение использовать еще один массив для записи результата через movaps эквивалентен тому, что вместо 2-х больших массивов будут читаться 3, т.к. прежде чем записать что-то в 3-й массив процессор обязан его прочитать из памяти в кэш. Поэтому если уж использовать отд.массив, то писать в него нужно не через movaps, а через movntdq

    Ничего не противоречит. Если все три вх.массива выравнены на 64, то в каждом цикле все 3 чтения производятся из одного и того же банка, поэтому в каждом цикле есть еще 3 свободных банка, в которые может производиться запись результата (запись, как я говорил идет с задержкой). Если же все 3 чтения производятся из разных банков, то вероятность нарваться на банк, занятый записью ес-но выше. Однако, из-за того, что запись производится с задержкой, предугадать все эти хитрости с конфликами банков практически невозможно, поэтому и заморачиваться с этим не стоит. Тем более, что в данном сл. на одну запись приходится 3 чтения и еще с десяток операций, поэтому возможный пропуск 1 такта из-за конфликта практической роли не играет
     
  12. murder

    murder Member

    Публикаций:
    0
    Регистрация:
    3 июн 2007
    Сообщения:
    628
    Развернуть цикл ещё не предлагали?
     
  13. serj

    serj New Member

    Публикаций:
    0
    Регистрация:
    27 сен 2005
    Сообщения:
    13
    (sorry, что поднимаю - тема старая, но интересная. :) )
    Несколько наблюдений.
    1. использование SSE ONLY может привести к жуткому падению производительности. В частности, К7-К8 (AMD естественно) просто помрут на SSE. Используйте MMX. Если позволяет алгоритм.
    2. не забывайте о количестве конвейеров SSE. У Core их 3. Т.е., теоретически можно получить IPC 3. Я получил 2.7-2.9; linpack - порядка 2.7 для х86 и 2.9 для х64. (почему так - см. ниже)
    Почему я вспомнл о '3'? Да просто потому, что _каждый_тик_ надо поставлять ТРИ инструкции.
    Выражусь несколько иначе - Вы должны представить 3 инструкции (три НЕЗАВИСИМЫЕ) инструкции, чтобы они выполнились за 1 тик.
    3. независимость.
    Параллельно выполняются независимые инструкции. Если есть зависимость в регистрах, то начинается 'секс'.
    Итак, из п2 и 3 следует, что алгоритм должен использовать операции с независимыми регистрами и этих операций надо аж 3 штуки за раз.
    4. предвыборка. Точнее загрузка.
    Не используйте операции выполнения с загрузкой из памяти.
    _вначале_ загружайте регистр, а уж затем его используйте. Между загрузкой регистра и его использованием надо делать БольшуюЗадержку на загрузку из кеши. С учетом кол-ва конвейеров это будет ОченьМного инструкций.
    5. Вывод.
    Максимально распараллеливайте вычисления.
    Например, алгоритм 'сумма числ в памяти' напрашивается в виде цикла 'reg+[mem]'.
    Однако, если разрезать его на цикл ('reg0+[mem]' 'reg1+[mem]' ...) и потом сложить эти регистры reg(i), то процедура будет работать быстрее. Почему? А потому, что алгоритм будет выглядить так:
    reg0<-mem0
    reg1<-mem1
    reg2<-mem2
    reg4=reg4+reg0
    reg3<-mem3
    reg5=reg5+reg1
    reg6=reg6+reg2
    reg0<-mem0
    reg7=reg7+reg3
    reg1<-mem1
    reg2<-mem2
    Между загрузкой регистра и его использованием проходит 3 инструкции. Та-же задержка и при новом использовании этого-же регистра.

    6. И из неприятного. Как видно, регистров SSE фатально не хватает. По используйте MMX. Хоть и мелкие, но подходят для промежуточный констант.
    В режиме х64 кол-во SSE регистров удваивается, поэтому (и только поэтому, IMHO) в х64 SSE может быть быстрее.
    Для упомянутого linpack написаны разнличающиеся алгоритмы по х86 и х64 редакциям. Для х86 4 параллельных вычислений, для х64 их число в 2 раза больше и поэтому IPC больше (значительно меньше взаимные конфликты использования регистров).

    Ну и, чтоб небыло чистой говорильни, фрагмент linpack для медитации. :)
    Код (Text):
    1.         movaps  xmm7, oword ptr [eax+40h]
    2.         movaps  xmm6, oword ptr [ebx]
    3.         movaps  xmm5, xmm6
    4.         mulpd   xmm6, xmm4
    5.         addpd   xmm0, xmm6
    6.         movaps  xmm6, oword ptr [ebx+10h]
    7.         mulpd   xmm4, xmm6
    8.         addpd   xmm1, xmm4
    9.         movaps  xmm4, oword ptr [eax+10h]
    10.         mulpd   xmm5, xmm4
    11.         addpd   xmm2, xmm5
    12.         mulpd   xmm6, xmm4
    13.         addpd   xmm3, xmm6
    14.         movaps  xmm4, oword ptr [eax+20h]
    15.         movaps  xmm6, oword ptr [ebx+20h]
    16.         movaps  xmm5, xmm6
    17.         mulpd   xmm6, xmm4
    18.         addpd   xmm0, xmm6
    19.         movaps  xmm6, oword ptr [ebx+30h]
    20.         mulpd   xmm4, xmm6
    21.         addpd   xmm1, xmm4
    22.         movaps  xmm4, oword ptr [eax+30h]
    23.         mulpd   xmm5, xmm4
    24.         addpd   xmm2, xmm5
    25.         mulpd   xmm6, xmm4
    26.         addpd   xmm3, xmm6
    27.         movaps  xmm4, oword ptr [eax+80h]
    28.         movaps  xmm6, oword ptr [ebx+40h]
    29.         movaps  xmm5, xmm6
    30.         mulpd   xmm6, xmm7
    31.         addpd   xmm0, xmm6
    32.         movaps  xmm6, oword ptr [ebx+50h]
    33.         mulpd   xmm7, xmm6
    34.         addpd   xmm1, xmm7
    35.         movaps  xmm7, oword ptr [eax+50h]
    36.         mulpd   xmm5, xmm7
    37.         addpd   xmm2, xmm5
    38.         mulpd   xmm6, xmm7
    39.         addpd   xmm3, xmm6
    40.         movaps  xmm7, oword ptr [eax+60h]
    41.         movaps  xmm6, oword ptr [ebx+60h]
    42.         movaps  xmm5, xmm6
    43.         mulpd   xmm6, xmm7
    44.         addpd   xmm0, xmm6
    45.         movaps  xmm6, oword ptr [ebx+70h]
    46.         mulpd   xmm7, xmm6
    47.         addpd   xmm1, xmm7
    48.         movaps  xmm7, oword ptr [eax+70h]
    49.         mulpd   xmm5, xmm7
    50.         addpd   xmm2, xmm5
    51.         mulpd   xmm6, xmm7
    52.         addpd   xmm3, xmm6
    53.         movaps  xmm7, oword ptr [eax+0C0h]
    54.         movaps  xmm6, oword ptr [ebx+80h]
    55.         movaps  xmm5, xmm6
    56.         mulpd   xmm6, xmm4
    57.         addpd   xmm0, xmm6
    58.         movaps  xmm6, oword ptr [ebx+90h]
    59.         mulpd   xmm4, xmm6
    60.         addpd   xmm1, xmm4
    61.         movaps  xmm4, oword ptr [eax+90h]
    62.         mulpd   xmm5, xmm4
    63.         addpd   xmm2, xmm5
    64.         mulpd   xmm6, xmm4
    65.         addpd   xmm3, xmm6
    66.         movaps  xmm4, oword ptr [eax+0A0h]
    67.         movaps  xmm6, oword ptr [ebx+0A0h]
    68.         movaps  xmm5, xmm6
    69.         mulpd   xmm6, xmm4
    70.         addpd   xmm0, xmm6
    71.         movaps  xmm6, oword ptr [ebx+0B0h]
    72.         mulpd   xmm4, xmm6
    73.         addpd   xmm1, xmm4
    74.         movaps  xmm4, oword ptr [eax+0B0h]
    75.         mulpd   xmm5, xmm4
    76.         addpd   xmm2, xmm5
    77.         mulpd   xmm6, xmm4
    78.         addpd   xmm3, xmm6
    79.         movaps  xmm4, oword ptr [eax+100h]
    80.         movaps  xmm6, oword ptr [ebx+0C0h]
    81.         movaps  xmm5, xmm6
    82.         mulpd   xmm6, xmm7
    83.         addpd   xmm0, xmm6
    84.         movaps  xmm6, oword ptr [ebx+0D0h]
    85.         mulpd   xmm7, xmm6
    86.         addpd   xmm1, xmm7
    87.         movaps  xmm7, oword ptr [eax+0D0h]
    88.         mulpd   xmm5, xmm7
    89.         addpd   xmm2, xmm5
    90.         mulpd   xmm6, xmm7
    91.         addpd   xmm3, xmm6
    92.         movaps  xmm7, oword ptr [eax+0E0h]
    93.         movaps  xmm6, oword ptr [ebx+0E0h]
    94.         movaps  xmm5, xmm6
    95.         mulpd   xmm6, xmm7
    96.         addpd   xmm0, xmm6
    97.         movaps  xmm6, oword ptr [ebx+0F0h]
    98.         mulpd   xmm7, xmm6
    99.         addpd   xmm1, xmm7
    100.         movaps  xmm7, oword ptr [eax+0F0h]
    101.         mulpd   xmm5, xmm7
    102.         addpd   xmm2, xmm5
    103.         mulpd   xmm6, xmm7
    104.         addpd   xmm3, xmm6
    105.         movaps  xmm7, oword ptr [eax+140h]
    106.         movaps  xmm6, oword ptr [ebx+100h]
    107.         movaps  xmm5, xmm6
    108.         mulpd   xmm6, xmm4
    109.         addpd   xmm0, xmm6
    110.         movaps  xmm6, oword ptr [ebx+110h]
    111.         mulpd   xmm4, xmm6
    112.         addpd   xmm1, xmm4
    113.         movaps  xmm4, oword ptr [eax+110h]
    114.         mulpd   xmm5, xmm4
    115.         addpd   xmm2, xmm5
    116.         mulpd   xmm6, xmm4
    117.         addpd   xmm3, xmm6
    118.         movaps  xmm4, oword ptr [eax+120h]
    119.         movaps  xmm6, oword ptr [ebx+120h]
    120.         movaps  xmm5, xmm6
    121.         mulpd   xmm6, xmm4
    122.         addpd   xmm0, xmm6
    123.         movaps  xmm6, oword ptr [ebx+130h]
    124.         mulpd   xmm4, xmm6
    125.         addpd   xmm1, xmm4
    126.         movaps  xmm4, oword ptr [eax+130h]
    127.         mulpd   xmm5, xmm4
    128.         addpd   xmm2, xmm5
    129.         mulpd   xmm6, xmm4
    130.         addpd   xmm3, xmm6
    131.         movaps  xmm4, oword ptr [eax+180h]
    132.         movaps  xmm6, oword ptr [ebx+140h]
    133.         movaps  xmm5, xmm6
    134.         mulpd   xmm6, xmm7
    135.         addpd   xmm0, xmm6
    136.         movaps  xmm6, oword ptr [ebx+150h]
    137.         mulpd   xmm7, xmm6
    138.         addpd   xmm1, xmm7
    139.         movaps  xmm7, oword ptr [eax+150h]
    140.         mulpd   xmm5, xmm7
    141.         addpd   xmm2, xmm5
    142.         mulpd   xmm6, xmm7
    143.         addpd   xmm3, xmm6
    144.         movaps  xmm7, oword ptr [eax+160h]
    145.         movaps  xmm6, oword ptr [ebx+160h]
    146.         movaps  xmm5, xmm6
    147.         mulpd   xmm6, xmm7
    148.         addpd   xmm0, xmm6
    149.         movaps  xmm6, oword ptr [ebx+170h]
    150.         mulpd   xmm7, xmm6
    151.         addpd   xmm1, xmm7
    152.         movaps  xmm7, oword ptr [eax+170h]
    153.         mulpd   xmm5, xmm7
    154.         addpd   xmm2, xmm5
    155.         mulpd   xmm6, xmm7
    156.         addpd   xmm3, xmm6
    157.         movaps  xmm7, oword ptr [eax+1C0h]
    158.         movaps  xmm6, oword ptr [ebx+180h]
    159.         movaps  xmm5, xmm6
    160.         mulpd   xmm6, xmm4
    161.         addpd   xmm0, xmm6
    162.         movaps  xmm6, oword ptr [ebx+190h]
    163.         mulpd   xmm4, xmm6
    164.         addpd   xmm1, xmm4
    165.         movaps  xmm4, oword ptr [eax+190h]
    166.         mulpd   xmm5, xmm4
    167.         addpd   xmm2, xmm5
    168.         mulpd   xmm6, xmm4
    169.         addpd   xmm3, xmm6
    170.         movaps  xmm4, oword ptr [eax+1A0h]
    171.         movaps  xmm6, oword ptr [ebx+1A0h]
    172.         movaps  xmm5, xmm6
    173.         mulpd   xmm6, xmm4
    174.         addpd   xmm0, xmm6
    175.         movaps  xmm6, oword ptr [ebx+1B0h]
    176.         mulpd   xmm4, xmm6
    177.         addpd   xmm1, xmm4
    178.         movaps  xmm4, oword ptr [eax+1B0h]
    179.         mulpd   xmm5, xmm4
    180.         addpd   xmm2, xmm5
    181.         mulpd   xmm6, xmm4
    182.         addpd   xmm3, xmm6
    183.         movaps  xmm4, oword ptr [eax+200h]
    184.         movaps  xmm6, oword ptr [ebx+1C0h]
    185.         movaps  xmm5, xmm6
    186.         mulpd   xmm6, xmm7
    187.         addpd   xmm0, xmm6
    188.         movaps  xmm6, oword ptr [ebx+1D0h]
    189.         mulpd   xmm7, xmm6
    190.         addpd   xmm1, xmm7
    191.         movaps  xmm7, oword ptr [eax+1D0h]
    192.         mulpd   xmm5, xmm7
    193.         addpd   xmm2, xmm5
    194.         mulpd   xmm6, xmm7
    195.         addpd   xmm3, xmm6
    196.         movaps  xmm7, oword ptr [eax+1E0h]
    197.         movaps  xmm6, oword ptr [ebx+1E0h]
    198.         movaps  xmm5, xmm6
    199.         mulpd   xmm6, xmm7
    200.         addpd   xmm0, xmm6
    201.         movaps  xmm6, oword ptr [ebx+1F0h]
    202.         mulpd   xmm7, xmm6
    203.         addpd   xmm1, xmm7
    204.         movaps  xmm7, oword ptr [eax+1F0h]
    205.         mulpd   xmm5, xmm7
    206.         addpd   xmm2, xmm5
    207.         mulpd   xmm6, xmm7
    208.         addpd   xmm3, xmm6
    209.         movaps  xmm7, oword ptr [eax+240h]
    210.         movaps  xmm6, oword ptr [ebx+200h]
    211.         movaps  xmm5, xmm6
    212.         mulpd   xmm6, xmm4
    213.         addpd   xmm0, xmm6
    214.         movaps  xmm6, oword ptr [ebx+210h]
    215.         mulpd   xmm4, xmm6
    216.         addpd   xmm1, xmm4
    217.         movaps  xmm4, oword ptr [eax+210h]
    218.         mulpd   xmm5, xmm4
    219.         addpd   xmm2, xmm5
    220.         mulpd   xmm6, xmm4
    221.         addpd   xmm3, xmm6
    222.         movaps  xmm4, oword ptr [eax+220h]
    223.         movaps  xmm6, oword ptr [ebx+220h]
    224.         movaps  xmm5, xmm6
    225.         mulpd   xmm6, xmm4
    226.         addpd   xmm0, xmm6
    227.         movaps  xmm6, oword ptr [ebx+230h]
    228.         mulpd   xmm4, xmm6
    229.         addpd   xmm1, xmm4
    230.         movaps  xmm4, oword ptr [eax+230h]
    231.         mulpd   xmm5, xmm4
    232.         addpd   xmm2, xmm5
    233.         mulpd   xmm6, xmm4
    234.         addpd   xmm3, xmm6
    235.         movaps  xmm4, oword ptr [eax+280h]
    236.         movaps  xmm6, oword ptr [ebx+240h]
    237.         movaps  xmm5, xmm6
    238.         mulpd   xmm6, xmm7
    239.         addpd   xmm0, xmm6
    240.         movaps  xmm6, oword ptr [ebx+250h]
    241.         mulpd   xmm7, xmm6
    242.         addpd   xmm1, xmm7
    243.         movaps  xmm7, oword ptr [eax+250h]
    244.         mulpd   xmm5, xmm7
    245.         addpd   xmm2, xmm5
    246.         mulpd   xmm6, xmm7
    247.         addpd   xmm3, xmm6
    248.         movaps  xmm7, oword ptr [eax+260h]
    249.         movaps  xmm6, oword ptr [ebx+260h]
    250.         movaps  xmm5, xmm6
    251.         mulpd   xmm6, xmm7
    252.         addpd   xmm0, xmm6
    253.         movaps  xmm6, oword ptr [ebx+270h]
    254.         mulpd   xmm7, xmm6
    255.         addpd   xmm1, xmm7
    256.         movaps  xmm7, oword ptr [eax+270h]
    257.         mulpd   xmm5, xmm7
    258.         addpd   xmm2, xmm5
    259.         mulpd   xmm6, xmm7