Оптимизация времени выполнения процедуры.

Тема в разделе "WASM.BEGINNERS", создана пользователем Span, 31 дек 2008.

  1. Span

    Span New Member

    Публикаций:
    0
    Регистрация:
    5 ноя 2006
    Сообщения:
    134
    Доброго всем дня и с Наступающим!!!

    Никогда до этого не занимался оптимизацией на низком уровне... Решил написать сюда и почитать мнения гуру, т.к. время выполнения программы просто зашкаливает, а своих идей нет...

    Есть простая процедура на С++:

    Код (Text):
    1. inline FLOATING_VAR Neuron::calculate()
    2. {
    3.     //this will calculate output
    4.     sum = 0;
    5.     for (int i = 0; i < inputs; i++)
    6.         sum+=sinapsData[i] * inputData[i];
    7.  
    8.     //add polarization
    9.     sum+=sinapsData[inputs];
    10.     axon = sigmoid(sum);
    11.     return axon;
    12.  
    13. }
    , где
    - FLOATING_VAR - это просто define float
    - sum, axon - переменные типа float, не определены
    - inputs - переменная int=1024
    - sinapsData, inputData - массивы float размером 1025, заранее определены

    Эта процедура выполняется очень много раз в процессе работы программы (~10^6-8).

    Вопрос номер 1: Можно ли в этом коде хоть что-нибудь оптимизировать c т.з. скорости средствами Cpp?

    Насколько я понимаю - самое "узкое место" в этой процедуре - это цикл, в котором выполняются операции умножения. (for (int i = 0; i < inputs; i++) ...)

    Почитав соседний топик про SSE, решил наконец разобраться с этой технологией. Думал так:
    у меня есть 2 массива, размерностью 1024, которые нужно перемножить. Буду делать не в цикле по каждому элементу, а разобью массивы на группы по 16 элементов, чтобы влезло в регистры XMM. Затем все перемножу и сложу.

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

    Отсюда вопрос номер 2: почему код, не оптимизированный, и работающий с сопроцессором, работает быстрее в 5 раз чем мой, через SSE.

    Знающие люди, подскажите!

    Ниже привожу листинги:
    Код (Text):
    1. 1) Листинг дизассемблированного кода процедуры (которая была выше)
    2. inline FLOATING_VAR Neuron::calculate()
    3. {
    4. 00401090  push        ecx  
    5. 00401091  push        esi  
    6. 00401092  push        edi  
    7. 00401093  mov         esi,ecx
    8.     //this will calculate output
    9.     _asm int 3
    10. 00401095  int         3    
    11.     sum = 0;
    12. 00401096  fldz            
    13.     for (int i = 0; i < inputs; i++)
    14. 00401098  mov         edi,dword ptr [esi+18h]
    15. 0040109B  fstp        dword ptr [esi]
    16. 0040109D  xor         ecx,ecx
    17. 0040109F  test        edi,edi
    18. 004010A1  jle         Neuron::calculate+33h (4010C3h)
    19. 004010A3  mov         eax,dword ptr [esi+10h]
    20. 004010A6  mov         edx,dword ptr [esi+14h]
    21. 004010A9  sub         edx,eax
    22. 004010AB  jmp         Neuron::calculate+20h (4010B0h)
    23. 004010AD  lea         ecx,[ecx]
    24.         sum+=sinapsData[i] * inputData[i];
    25. 004010B0  fld         dword ptr [edx+eax]
    26. 004010B3  add         ecx,1
    27. 004010B6  fmul        dword ptr [eax]
    28. 004010B8  add         eax,4
    29. 004010BB  cmp         ecx,edi
    30. 004010BD  fadd        dword ptr [esi]
    31. 004010BF  fstp        dword ptr [esi]
    32. 004010C1  jl          Neuron::calculate+20h (4010B0h)
    33.  
    34.     //add polarization
    35.     sum+=sinapsData[inputs];
    36. 004010C3  mov         eax,dword ptr [esi+10h]
    37. 004010C6  fld         dword ptr [eax+edi*4]
    38. 004010C9  fadd        dword ptr [esi]
    39. 004010CB  fstp        dword ptr [esp+8]
    40. 004010CF  fld         dword ptr [esp+8]
    41. 004010D3  fst         dword ptr [esi]
    42.     axon = sigmoid(sum);
    43. ---
    44.     return axon;
    45.  
    46. }
    2) листинг того, что я написал на асме (вставки в C++) с целью оптимизации по скорости, используя SSE:
    Не читайте эту чушь, уже переписал.
    Код (Text):
    1. inline FLOATING_VAR Neuron::calculate()
    2. {
    3.     //this will calculate output
    4.     _asm int 3
    5.     sum = 0;
    6. 00401097  fldz            
    7.    
    8.     // split by groups for XMM regs
    9.     int groups = inputs / 16;
    10. 00401099  mov         eax,dword ptr [esi+18h]
    11. 0040109C  fstp        dword ptr [esi]
    12.     FLOATING_VAR* sinapsd = sinapsData;
    13. 0040109E  mov         ecx,dword ptr [esi+10h]
    14. 004010A1  cdq              
    15. 004010A2  and         edx,0Fh
    16. 004010A5  add         eax,edx
    17.     FLOATING_VAR* inputd = inputData;
    18. 004010A7  mov         edx,dword ptr [esi+14h]
    19. 004010AA  sar         eax,4
    20.     FLOATING_VAR  sumd0[4];
    21.     FLOATING_VAR  sumd1[4];
    22.     FLOATING_VAR  sumd2[4];
    23.     FLOATING_VAR  sumd3[4];
    24.  
    25.     // mul
    26.     for (int i = 0; i < groups; i++){
    27. 004010AD  test        eax,eax
    28. 004010AF  mov         dword ptr [esp+58h],ecx
    29. 004010B3  mov         dword ptr [esp+48h],edx
    30. 004010B7  jle         Neuron::calculate+195h (401225h)
    31.     //this will calculate output
    32.     _asm int 3
    33. 004010BD  mov         ecx,eax
    34. 004010BF  nop              
    35.         _asm {
    36.  
    37.             // 0 and 1
    38.             movups xmm0, sinapsd; // поместить 4 переменные с плавающей точкой в регистр xmm0
    39. 004010C0  movups      xmm0,xmmword ptr [esp+58h]
    40.             movups xmm1, inputd; // поместить 4 переменные с плавающей точкой в регистр xmm1
    41. 004010C5  movups      xmm1,xmmword ptr [esp+48h]
    42.             // sinapsd += 4
    43.             mov         eax,dword ptr [sinapsd];
    44. 004010CA  mov         eax,dword ptr [esp+58h]
    45.             add         eax,10h;
    46. 004010CE  add         eax,10h
    47.             mov         dword ptr [sinapsd],eax;
    48. 004010D1  mov         dword ptr [esp+58h],eax
    49.             // inputd += 4
    50.             mov         eax,dword ptr [inputd];
    51. 004010D5  mov         eax,dword ptr [esp+48h]
    52.             add         eax,10h;
    53. 004010D9  add         eax,10h
    54.             mov         dword ptr [inputd],eax;
    55. 004010DC  mov         dword ptr [esp+48h],eax
    56.  
    57.             // 2 and 3
    58.             movups xmm2, sinapsd; // поместить 4 переменные с плавающей точкой в регистр xmm2
    59. 004010E0  movups      xmm2,xmmword ptr [esp+58h]
    60.             movups xmm3, inputd; // поместить 4 переменные с плавающей точкой в регистр xmm3
    61. 004010E5  movups      xmm3,xmmword ptr [esp+48h]
    62.             // sinapsd += 4
    63.             mov         eax,dword ptr [sinapsd];
    64. 004010EA  mov         eax,dword ptr [esp+58h]
    65.             add         eax,10h;
    66. 004010EE  add         eax,10h
    67.             mov         dword ptr [sinapsd],eax;
    68. 004010F1  mov         dword ptr [esp+58h],eax
    69.             // inputd += 4
    70.             mov         eax,dword ptr [inputd];
    71. 004010F5  mov         eax,dword ptr [esp+48h]
    72.             add         eax,10h;
    73. 004010F9  add         eax,10h
    74.             mov         dword ptr [inputd],eax;
    75. 004010FC  mov         dword ptr [esp+48h],eax
    76.  
    77.             // 4 and 5
    78.             movups xmm4, sinapsd; // поместить 4 переменные с плавающей точкой в регистр xmm4
    79. 00401100  movups      xmm4,xmmword ptr [esp+58h]
    80.             movups xmm5, inputd; // поместить 4 переменные с плавающей точкой в регистр xmm5
    81. 00401105  movups      xmm5,xmmword ptr [esp+48h]
    82.             // sinapsd += 4
    83.             mov         eax,dword ptr [sinapsd];
    84. 0040110A  mov         eax,dword ptr [esp+58h]
    85.             add         eax,10h;
    86. 0040110E  add         eax,10h
    87.             mov         dword ptr [sinapsd],eax;
    88. 00401111  mov         dword ptr [esp+58h],eax
    89.             // inputd += 4
    90.             mov         eax,dword ptr [inputd];
    91. 00401115  mov         eax,dword ptr [esp+48h]
    92.             add         eax,10h;
    93. 00401119  add         eax,10h
    94.             mov         dword ptr [inputd],eax;
    95. 0040111C  mov         dword ptr [esp+48h],eax
    96.  
    97.             // 6 and 7
    98.             movups xmm6, sinapsd; // поместить 4 переменные с плавающей точкой в регистр xmm6
    99. 00401120  movups      xmm6,xmmword ptr [esp+58h]
    100.             movups xmm7, inputd; // поместить 4 переменные с плавающей точкой в регистр xmm7
    101. 00401125  movups      xmm7,xmmword ptr [esp+48h]
    102.             // sinapsd += 4
    103.             mov         eax,dword ptr [sinapsd];
    104. 0040112A  mov         eax,dword ptr [esp+58h]
    105.             add         eax,10h;
    106. 0040112E  add         eax,10h
    107.             mov         dword ptr [sinapsd],eax;
    108. 00401131  mov         dword ptr [esp+58h],eax
    109.             // inputd += 4
    110.             mov         eax,dword ptr [inputd];
    111. 00401135  mov         eax,dword ptr [esp+48h]
    112.             add         eax,10h;
    113. 00401139  add         eax,10h
    114.             mov         dword ptr [inputd],eax;
    115. 0040113C  mov         dword ptr [esp+48h],eax
    116.  
    117.             // now do mul
    118.             mulps xmm1, xmm0; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0
    119. 00401140  mulps       xmm1,xmm0
    120.             mulps xmm3, xmm2; // перемножить пакеты плавающих точек: xmm3=xmm3*xmm2
    121. 00401143  mulps       xmm3,xmm2
    122.             mulps xmm5, xmm4; // перемножить пакеты плавающих точек: xmm5=xmm5*xmm4
    123. 00401146  mulps       xmm5,xmm4
    124.             mulps xmm7, xmm6; // перемножить пакеты плавающих точек: xmm7=xmm7*xmm6
    125. 00401149  mulps       xmm7,xmm6
    126.  
    127.             // store results
    128.             movups sumd0, xmm1; // выгрузить результаты из регистра xmm1
    129. 0040114C  movups      xmmword ptr [esp+8],xmm1
    130.             movups sumd1, xmm3; // выгрузить результаты из регистра xmm1
    131. 00401151  movups      xmmword ptr [esp+18h],xmm3
    132.             movups sumd2, xmm5; // выгрузить результаты из регистра xmm1
    133. 00401156  movups      xmmword ptr [esp+28h],xmm5
    134.             movups sumd3, xmm7; // выгрузить результаты из регистра xmm1
    135. 0040115B  movups      xmmword ptr [esp+38h],xmm7
    136.         };
    137.  
    138.         // add to sum
    139.         sum += sumd0[0];sum += sumd0[1];sum += sumd0[2];sum += sumd0[3];
    140.         sum += sumd1[0];sum += sumd1[1];sum += sumd1[2];sum += sumd1[3];
    141.         sum += sumd2[0];sum += sumd2[1];sum += sumd2[2];sum += sumd2[3];
    142.         sum += sumd3[0];sum += sumd3[1];sum += sumd3[2];sum += sumd3[3];
    143. 00401160  fld         dword ptr [esi]
    144. 00401162  sub         ecx,1
    145. 00401165  fadd        dword ptr [esp+8]
    146. 00401169  fstp        dword ptr [esp+4]
    147. 0040116D  fld         dword ptr [esp+4]
    148. 00401171  fadd        dword ptr [esp+0Ch]
    149. 00401175  fstp        dword ptr [esp+4]
    150. 00401179  fld         dword ptr [esp+4]
    151. 0040117D  fadd        dword ptr [esp+10h]
    152. 00401181  fstp        dword ptr [esp+4]
    153. 00401185  fld         dword ptr [esp+4]
    154. 00401189  fadd        dword ptr [esp+14h]
    155. 0040118D  fstp        dword ptr [esp+4]
    156. 00401191  fld         dword ptr [esp+4]
    157. 00401195  fadd        dword ptr [esp+18h]
    158. 00401199  fstp        dword ptr [esp+4]
    159. 0040119D  fld         dword ptr [esp+4]
    160. 004011A1  fadd        dword ptr [esp+1Ch]
    161. 004011A5  fstp        dword ptr [esp+4]
    162. 004011A9  fld         dword ptr [esp+4]
    163. 004011AD  fadd        dword ptr [esp+20h]
    164. 004011B1  fstp        dword ptr [esp+4]
    165. 004011B5  fld         dword ptr [esp+4]
    166. 004011B9  fadd        dword ptr [esp+24h]
    167. 004011BD  fstp        dword ptr [esp+4]
    168. 004011C1  fld         dword ptr [esp+4]
    169. 004011C5  fadd        dword ptr [esp+28h]
    170. 004011C9  fstp        dword ptr [esp+4]
    171. 004011CD  fld         dword ptr [esp+4]
    172. 004011D1  fadd        dword ptr [esp+2Ch]
    173. 004011D5  fstp        dword ptr [esp+4]
    174. 004011D9  fld         dword ptr [esp+4]
    175. 004011DD  fadd        dword ptr [esp+30h]
    176. 004011E1  fstp        dword ptr [esp+4]
    177. 004011E5  fld         dword ptr [esp+4]
    178. 004011E9  fadd        dword ptr [esp+34h]
    179. 004011ED  fstp        dword ptr [esp+4]
    180. 004011F1  fld         dword ptr [esp+4]
    181. 004011F5  fadd        dword ptr [esp+38h]
    182. 004011F9  fstp        dword ptr [esp+4]
    183. 004011FD  fld         dword ptr [esp+4]
    184. 00401201  fadd        dword ptr [esp+3Ch]
    185. 00401205  fstp        dword ptr [esp+4]
    186. 00401209  fld         dword ptr [esp+4]
    187. 0040120D  fadd        dword ptr [esp+40h]
    188. 00401211  fstp        dword ptr [esp+4]
    189. 00401215  fld         dword ptr [esp+4]
    190. 00401219  fadd        dword ptr [esp+44h]
    191. 0040121D  fstp        dword ptr [esi]
    192. 0040121F  jne         Neuron::calculate+30h (4010C0h)
    193.     }
    194.    
    195.  
    196.     //add polarization
    197.     sum+=sinapsData[inputs];
    198. 00401225  mov         eax,dword ptr [esi+18h]
    199. 00401228  mov         ecx,dword ptr [esi+10h]
    200. 0040122B  fld         dword ptr [ecx+eax*4]
    201. 0040122E  fadd        dword ptr [esi]
    202. 00401230  fstp        dword ptr [esp+4]
    203. 00401234  fld         dword ptr [esp+4]
    204. 00401238  fst         dword ptr [esi]
    205.     axon = sigmoid(sum);
    206. 0040123A  fmul        qword ptr [__real@bff0000000000000 (402180h)]
    207. 00401240  fstp        dword ptr [esp+4]
    208. 00401244  fld         dword ptr [esp+4]
    209. 00401248  call        _CIexp (401E80h)
    210. 0040124D  fstp        dword ptr [esp+4]
    211. 00401251  fld         dword ptr [esp+4]
    212. 00401255  fld1            
    213. 00401257  fadd        st(1),st
    214. 00401259  fdivrp      st(1),st
    215. 0040125B  fstp        dword ptr [esp+4]
    216. 0040125F  fld         dword ptr [esp+4]
    217. 00401263  fst         dword ptr [esi+4]
    218. 00401266  pop         esi  
    219.  
    220.     return axon;
    221.  
    222. }
    Компилятор - MS VS2005.
    Процессор - Core2 QUAD.
    тестирую в 32, в боевую должно работать в 64, т.к. много памяти требуется.

    Спасибо всем.
     
  2. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Оптимизировать можно и на fpu и на SSE путем распараллеливания вычислений, т.е. нужно копить не одну сумму sum, а 4 независимые подсуммы и затем по окончании цикла сложить их в общую sum. А твоя SSE-реализация какая-то коряво-половинчатая, т.к. после векторных умножений не нужно ничего выгружать в память и складывать подсуммы на fpu (это дает доп.тормоза из-за store-to-load forwarding restriction-а, т.к. нельзя читать дворды из середины только что сохраненного 16-байтного числа). Нужно просто отвести один (или несколько) регистров под суммы и прибавлять к нему умноженные значения, накапливая таким образом 4 подсуммы в одном регистре, и только по окончании всего цикла сложить между собой эти подсуммы или на SSE или на fpu (если цикл большой, то штрафом на STLF можно и пренебречь)
     
  3. Span

    Span New Member

    Публикаций:
    0
    Регистрация:
    5 ноя 2006
    Сообщения:
    134
    Не совсем понимаю. Речь идет о распараллеливании на каком уровне?

    Спасиб.
    Да, какую-то чушь написал вначале. Переделал. Теперь быстро считает))
    Не могу только найти команду, которая бы один XMM регистр складывала целиком (т.е. все 4 float числа одного регистра сложить).

    Код (Text):
    1.     sum = 0;
    2.     // split by groups for XMM regs
    3.     int groups = inputs / 16;
    4.     FLOATING_VAR  sumd[4];
    5.  
    6.     _asm {
    7.         mov edx, [esi + inputData];
    8.         mov ebx, [esi + sinapsData];
    9.     };
    10.  
    11.     // do SSE mul
    12.     for (int i = 0; i < groups; i++){
    13.         _asm {
    14.             // load data to XMM
    15.             movups xmm0, [ebx];     // поместить 4 переменные с плавающей точкой в регистр xmm0
    16.             movups xmm1, [edx];     // поместить 4 переменные с плавающей точкой в регистр xmm1
    17.             movups xmm2, [ebx+10h]; // поместить 4 переменные с плавающей точкой в регистр xmm2
    18.             movups xmm3, [edx+10h]; // поместить 4 переменные с плавающей точкой в регистр xmm3
    19.             movups xmm4, [ebx+20h]; // поместить 4 переменные с плавающей точкой в регистр xmm4
    20.             movups xmm5, [edx+20h]; // поместить 4 переменные с плавающей точкой в регистр xmm5
    21.             movups xmm6, [ebx+30h]; // поместить 4 переменные с плавающей точкой в регистр xmm6
    22.             movups xmm7, [edx+30h]; // поместить 4 переменные с плавающей точкой в регистр xmm7
    23.            
    24.             // XMM mul
    25.             mulps xmm1, xmm0; // перемножить пакеты плавающих точек: xmm1=xmm1*xmm0
    26.             mulps xmm3, xmm2; // перемножить пакеты плавающих точек: xmm3=xmm3*xmm2
    27.             mulps xmm5, xmm4; // перемножить пакеты плавающих точек: xmm5=xmm5*xmm4
    28.             mulps xmm7, xmm6; // перемножить пакеты плавающих точек: xmm7=xmm7*xmm6
    29.  
    30.             // XMM add 0 = (1 + 3) + (5 + 7)
    31.             addps xmm1, xmm3;
    32.             addps xmm5, xmm7;
    33.             addps xmm1, xmm5;
    34.             // Вот здесь бы сложить все 4 переменных float регистра XMM1.
    35.             // Не знаю как, поэтому выгружаем в память
    36.             // store results
    37.             movups sumd, xmm1;
    38.  
    39.             // increment registers
    40.             add ebx, 40h
    41.             add edx, 40h
    42.  
    43.         };
    44.         // add to sum
    45.         sum += sumd[0];
    46.         sum += sumd[1];
    47.         sum += sumd[2];
    48.         sum += sumd[3];
    49.     }
    Можно ли еще оптимизировать этот код по скорости?
     
  4. asmfan

    asmfan New Member

    Публикаций:
    0
    Регистрация:
    10 июл 2006
    Сообщения:
    1.004
    Адрес:
    Abaddon
    haddps*2
     
  5. scf

    scf Member

    Публикаций:
    0
    Регистрация:
    12 сен 2005
    Сообщения:
    386
    Как вариант, можно вообще отказаться от использования FPU
    Идея: работать с двоичными дробями, используя команды целочисленного сложения/умножения/деления
    Ключевые слова: масштаб, целочисленная арифметика, дробная арифметика, коррекция после умножения, коррекция перед делением
    Пример: возвести в квадрат x, x = [-200, 200]
    для 16-битных слов:
    целочисленный масштаб M=32767/200 = 2^7
    Масштаб результата My = M*M/2^16 = 2^-2, оценочный масштаб M = M(200*200) = 32767/40000 = 2^-1, коррекция на 1 бит (разница в масштабах)
    Возведем в квадрат 123.456:
    Код (Text):
    1. mov ax, 3DBAh ; 123.456 * 2^7
    2. imul ax
    3. shld dx, ax, 1
    4. ;здесь в dx - результат 1DC4
    5. ;результат = 1DC4h*2 = 15240
    Относительная ошибка = (15241,383936 - 15240)/15241,383936 = 0,0091%
    Работает все это очень быстро, возвращает малую ошибку и это при 16-битной разрядности (double использует 64-битную мантиссу)
     
  6. PSR1257

    PSR1257 New Member

    Публикаций:
    0
    Регистрация:
    30 ноя 2008
    Сообщения:
    933
    Условия задачи не позволяют заранее рассчитать:

    MasMul[]= sinapsData * inputData

    ?

    Какого рода числа в этих массивах? Абсолютно случайные?
     
  7. Span

    Span New Member

    Публикаций:
    0
    Регистрация:
    5 ноя 2006
    Сообщения:
    134
    То что надо. Спасибо.

    Идея хорошая, буду копать.
    Область значений чисел в массиве не определена. Там могут быть как очень близкие к нулю по модулю числа, так и большие. Придется всеравно таскать экспоненту за собой.


    А что такое MasMul в примере? Если это максимум - то нет.

    В одном массиве числа изначально случайные. Известно только, что они от 0 до 1.
    Во втором тоже случайные. Значения - любые вещественные числа.
     
  8. scf

    scf Member

    Публикаций:
    0
    Регистрация:
    12 сен 2005
    Сообщения:
    386
    Span
    Насчет диапазонов.
    Обрати внимание, что у тебя сложение. При сложении двух чисел, сильно отличающихся по порядку, точность одного из них практически не будет иметь значения. Если разница в экспоненте 2^10, то 10 младших бит меньшего из слагаемых в сложении участвовать не будут и будут отброшены.
    Так что надо думать и считать.
     
  9. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Span
    На уровне оптимизации кода - устранения зависимой операции sum+=..., путем разбивки ее на 4 независимых sumd[0]+=.., sumd[1]+=.. и т.д. Т.к. от перестановки мест слагаемых сумма не меняется, то можно независимо копить 4 суммы - в sumd[0] копим сумму [0]+[4]+[8]+.., в sumd[1] соотв-но [1]+[5]+[9]+.. и т.д. Затем после завершения цикла складываем части и получаем общую сумму. Для вещественных fpu и SSE операций такая разбивка дает заметный прирост скорости, т.к. латентность fadd и addps\addpd достаточно большая - порядка 5-7 тактов, но независимые сложения за счет конвееризации могут выполняться каждый такт, т.е. практически параллельно

    Можно. Во-первых, опбеспечить выравнивание массивов на 16 и заменить тормозные movups на быстрые movaps. Во-вторых, заменить загрузку вторых операндов и умножение reg*reg на прямое умножение reg*mem. В-третьих, прислушаться к совету и производить сложение частей не внутри цикла, а после его завершения
     
  10. murder

    murder Member

    Публикаций:
    0
    Регистрация:
    3 июн 2007
    Сообщения:
    628
    Код (Text):
    1. mov   edx,[inputs]
    2. shl   edx,2
    3. mov   ecx,edx
    4. neg   ecx
    5. lea   eax,[sinapsData+edx]
    6. lea   edx,[inputData+edx]
    7. xorps xmm0,xmm0
    8. movss xmm0,[eax]
    9. calc:movaps xmm1,[eax+ecx]
    10.      mulps  xmm1,[edx+ecx]
    11.      addps  xmm0,xmm1
    12.      add    ecx,16
    13. jne calc
    14. movhlps xmm1,xmm0
    15. addps   xmm0,xmm1
    16. movss   xmm1,xmm0
    17. shufps  xmm0,xmm0,1
    18. addss   xmm0,xmm1
    19. sub     esp,4
    20. movss   [esp],xmm0
    21. call    sigmoid
    22. mov     [axon],eax
    inputs должно быть кратно 4
     
  11. Span

    Span New Member

    Публикаций:
    0
    Регистрация:
    5 ноя 2006
    Сообщения:
    134
    murder
    Спасибо за код. Сижу разбираюсь с ним. Насколько я понял, в такой реализации операнд (адрес) для movaps всегда будет % 16.
     
  12. Span

    Span New Member

    Публикаций:
    0
    Регистрация:
    5 ноя 2006
    Сообщения:
    134
    Спасибо. Очень помогло. Даже не верится что считаться стало так быстро.

    Переписал с учетом этих советов, получилось вот что:
    Код (Text):
    1. PUBLIC neuron_calculate
    2. .CODE
    3.  
    4. ; RCX - pointer to 1st FLOAT array
    5. ; RDX - pointer to 2nd FLOAT array
    6. ; R8  - pointer to output FLOAT var
    7. ; R9  - Array size
    8.  
    9. neuron_calculate   PROC
    10.     ; iterations = inputs / 32
    11.     shr R9, 5
    12.    
    13.     ; XMM
    14.     ; 0 - 7  - first operands
    15.     ; 8 - 15 - SUMs
    16.     xorps xmm8, xmm8
    17.     xorps xmm9, xmm9
    18.     xorps xmm10, xmm10
    19.     xorps xmm11, xmm11
    20.     xorps xmm12, xmm12
    21.     xorps xmm13, xmm13
    22.     xorps xmm14, xmm14
    23.     xorps xmm15, xmm15
    24.    
    25. mul_loop:
    26.     cmp R9, 0
    27.     je mul_end
    28.  
    29.     ;load data to XMM
    30.     movaps xmm0, [rdx];     // поместить 4 переменные с плавающей точкой в регистры xmm
    31.     movaps xmm1, [rdx+10h];
    32.     movaps xmm2, [rdx+20h];
    33.     movaps xmm3, [rdx+30h];
    34.     movaps xmm4, [rdx+40h];
    35.     movaps xmm5, [rdx+50h];
    36.     movaps xmm6, [rdx+60h];
    37.     movaps xmm7, [rdx+70h];
    38.    
    39.     ; XMM mul
    40.     mulps xmm0, [rcx];     // перемножить пакеты переменных: xmm=xmm*[MEM]
    41.     mulps xmm1, [rcx+10h];
    42.     mulps xmm2, [rcx+20h];
    43.     mulps xmm3, [rcx+30h];
    44.     mulps xmm4, [rcx+40h];
    45.     mulps xmm5, [rcx+50h];
    46.     mulps xmm6, [rcx+60h];
    47.     mulps xmm7, [rcx+70h];
    48.  
    49.     ; add
    50.     addps xmm8, xmm0;   // добавить результат к накапливаемым частичным суммам
    51.     addps xmm9, xmm1;
    52.     addps xmm10, xmm2;
    53.     addps xmm11, xmm3;
    54.     addps xmm12, xmm4;
    55.     addps xmm13, xmm5;
    56.     addps xmm14, xmm6;
    57.     addps xmm15, xmm7;
    58.    
    59.     ; inc regs
    60.     add rcx, 80h;
    61.     add rdx, 80h;
    62.     dec R9
    63.  
    64.     ; goto start
    65.     jmp mul_loop
    66. mul_end:
    67.     ; calc result
    68.     addps xmm8, xmm9;
    69.     addps xmm10, xmm11;
    70.     addps xmm12, xmm13;
    71.     addps xmm14, xmm15;
    72.    
    73.     addps xmm8, xmm10;
    74.     addps xmm12, xmm14;
    75.    
    76.     addps xmm8, xmm12;
    77.    
    78.     haddps xmm8, xmm8
    79.     haddps xmm8, xmm8
    80.     movss DWORD PTR[R8], xmm8
    81.    ret
    82. neuron_calculate   ENDP
    83. END
    Имеет ли здесь смысл перемешать вручную команды (загрузки, умножения, сложения) в теле цикла???
    Цель - избавиться от зависимости при выполнении.
    Хаотичные перемещения не дали результата.
     
  13. asmfan

    asmfan New Member

    Публикаций:
    0
    Регистрация:
    10 июл 2006
    Сообщения:
    1.004
    Адрес:
    Abaddon
    Имеет смысл посмотреть ABI:
    Код (Text):
    1. ;  R12:R15    | Nonvolatile | preserved by callee
    2. ;  XMM6:XMM15 | Nonvolatile | preserved by callee
     
  14. Span

    Span New Member

    Публикаций:
    0
    Регистрация:
    5 ноя 2006
    Сообщения:
    134
    Т.Е. мне их (Nonvolatile) лучше не трогать в теле ф-и??
    А чем мне это грозит, если ф-ю вызывает моя же программа??
     
  15. murder

    murder Member

    Публикаций:
    0
    Регистрация:
    3 июн 2007
    Сообщения:
    628
    О-о-о-о rdx, xmm15, haddps...

    А у меня AMD Athlon XP 2500 :dntknw:

    1) Сделай адресацию как у меня - через один регистр.
    2) Если массивы большие попробуй подход AMD (Блочная предвыборка + movntps/sfence)
     
  16. Span

    Span New Member

    Публикаций:
    0
    Регистрация:
    5 ноя 2006
    Сообщения:
    134
    Чтож мне, из солидарности отказаться от этого? ))))
    Thx. Стало еще чуть быстрее.
    Ок, курю мануалы.
     
  17. Span

    Span New Member

    Публикаций:
    0
    Регистрация:
    5 ноя 2006
    Сообщения:
    134
    Спасибо. Узнал много нового.
    Хотя думал, что MASM все сделает за меня (сохранение значений используемых Nonvolatile регистров, +- RSP).

    А чем мне грозит отсутствие пролога/эпилога?
    Что, например, если я оставлю стек в покое в вызываемой процедуре (SPILL for args)?

    Я не заметил каких-либо изменений.
     
  18. murder

    murder Member

    Публикаций:
    0
    Регистрация:
    3 июн 2007
    Сообщения:
    628
    Ничем не грозит.

    AMD советует для эпилога использовать leave. А вот Enter для пролога не рекомендуется.