61.htm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. <!--
  2. demo.design 3D programming FAQ
  3. Idea, texts, screenshots:
  4. Andrew A. Aksyonoff,
  5. shodan@chat.ru
  6. Web-design, illustrations:
  7. Andrey Samoilov,
  8. asy@sense.simbirsk.su
  9. -->
  10. <html>
  11. <head>
  12. <title>demo.design 3D programming FAQ. Оптимизация. Приемы оптимизации для процессоров Intel Pentium.</title>
  13. <link rel=stylesheet href="../style.css" type="text/css">
  14. </head>
  15. <script language="javascript">
  16. <!--//
  17. browser = navigator.appName;
  18. version = parseFloat(navigator.appVersion);
  19. if (browser == "Netscape" && version >= 3.0) { jsenabled = 1; } else
  20. if (browser == "Microsoft Internet Explorer" && version >= 3.0) { jsenabled = 1; } else { jsenabled = 0; }
  21. function swap(img,ref) { if (jsenabled) {document.images[img].src = ref;} }
  22. function loadtocache(img,ref) { cache[img] = new Image(); cache[img].src = ref; }
  23. if (jsenabled) {
  24. cache = new Array();
  25. loadtocache(0,"../img/xdl.gif");
  26. loadtocache(1,"../img/xfaq.gif");
  27. loadtocache(2,"../img/xlinks.gif");
  28. loadtocache(3,"../img/xauthor.gif");
  29. loadtocache(4,"../img/xe.gif");
  30. loadtocache(5,"../img/xprev.gif");
  31. loadtocache(6,"../img/xnext.gif");}
  32. //-->
  33. </script>
  34. <body bgcolor=white><center>
  35. <!-- Title -->
  36. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  37. <img src="../img/t.gif" width=500 height=1 alt=""><br>
  38. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  39. <img src="../img/t.gif" width=500 height=2 alt=""><br>
  40. <table width=500 cellpadding=0 cellspacing=0 border=0>
  41. <td><img src="../img/t.gif" width=5 height=1 alt=""><a href="../main.htm" onmouseover="swap('logo','../img/xe.gif');" onmouseout="swap('logo','../img/e.gif');"><img src="../img/e.gif" name=logo width=60 height=50 hspace=10 border=0 alt=" в самое начало "></a></td>
  42. <td><p class=pagetitle><img src="../img/t.gif" width=265 height=1 alt=""><br>demo.design<br>3D programming FAQ</td>
  43. <td align=center><p class=navy><a href="../download.htm" onmouseover="swap('dl','../img/xdl.gif');" onmouseout="swap('dl','../img/dl.gif');"><img src="../img/dl.gif" name=dl width=40 height=40 border=0 hspace=5 alt=" download "></a><br>download</td>
  44. <td align=center><p class=navy><a href="../links.htm" onmouseover="swap('links','../img/xlinks.gif');" onmouseout="swap('links','../img/links.gif');"><img src="../img/links.gif" name=links width=40 height=40 border=0 hspace=5 alt=" коллекция линков "></a><br>links</td>
  45. <td align=center><p class=navy><a href="../author.htm" onmouseover="swap('author','../img/xauthor.gif');" onmouseout="swap('author','../img/author.gif');"><img src="../img/author.gif" name=author width=40 height=40 border=0 hspace=5 alt=" автора! "></a><br>author</td>
  46. </table>
  47. <img src="../img/t.gif" width=500 height=4 alt=""><br><img src="../img/b.gif" width=500 height=1 alt=""><br>
  48. <!-- Head -->
  49. <table width=500 cellpadding=0 cellspacing=10 border=0><td>
  50. <p class=title>
  51. <img src="../img/b6.gif" width=70 height=70 align=left hspace=0 alt="">
  52. <img src="../img/t.gif" width=5 height=70 align=left hspace=0 alt="">
  53. ОПТИМИЗАЦИЯ<br>6.1. Приемы оптимизации для процессоров Intel Pentium
  54. <div align=justify>
  55. <!-- Article -->
  56. <p>Все, что здесь написано, является выборкой наиболее важных на мой взгляд
  57. фактов из документации от Agner Fog. Если вы серьезно интересуетесь
  58. оптимизацией для Intel Pentium (plain, MMX, PPro, P2), найдите и прочтите
  59. эту документацию (я нашел на <a href="http://www.agner.org/assem">http://www.agner.org/assem</a>, относительно
  60. старая версия есть на <a href="ftp://ftp.cdrom.com/pub/sac/text/pentopt.zip">ftp://ftp.cdrom.com/pub/sac/text/pentopt.zip</a>).
  61. <div align=left>
  62. <a name="611"></a>
  63. <p class=title><img src="../img/b6.gif" width=70 height=70 align=left hspace=0 alt=""><img src="../img/t.gif" width=5 height=70 align=left hspace=0 alt="">6.1.1. Спаривание целочисленных команд
  64. <div align=justify>
  65. <p>По-моему, основной прием ускорения. Дело в том, что у процессоров Pentium
  66. есть два конвейера обработки команд, U-pipe и V-pipe. В результате некоторые
  67. пары команд могут исполняться одновременно, а это практически удваивает
  68. скорость.
  69. <p>Эти команды могут быть исполнены и в U-pipe, и в V-pipe, и при этом могут
  70. быть спарены (с какой-либо другой командой):
  71. <p class=expression>mov reg/mem,reg/mem/imm<br>
  72. push reg/imm<br>
  73. pop reg<br>
  74. lea, nop, inc, dec, add, sub, cmp, and, or, xor<br>
  75. некоторые формы test<br>
  76. <p>Эти команды могут быть исполнены только в U-pipe, но при этом все-таки могут
  77. быть спарены:
  78. <p class=expression>adc, sbb<br>
  79. shr, sar, shl, sal на заданное число<br>
  80. ror, rol, rcr, rcl на единичку<br>
  81. <p>Эти команды могут быть исполнены в любом конвейере, но могут быть спарены
  82. только в V-pipe:
  83. <p class=expression>near call (близкий вызов)<br>
  84. short/near jump (короткий/близкий переход)<br>
  85. short/near conditional jump (короткий/близкий переход по условию)<br>
  86. <p>Все остальные целочисленные команды могут быть исполнены только в U-pipe
  87. и не могут быть спарены вообще.
  88. <p>Две последовательно идущих команды будут спарены в случае выполнения всех
  89. нижеследующих условий. Если хотя бы одно из условий не выполняется, то
  90. исполняется только первая команда, вторая (и, возможно, следующая за ней)
  91. команда будет исполнена лишь в следующем такте. Вот условия спаривания:
  92. <ol>
  93. <li>Первая команда может быть исполнена и спарена в U-pipe, вторая,
  94. соответственно, в V-pipe.<br><br>
  95. <li>Если первая команда записывает что-то в регистр, то вторая команда не
  96. может производить чтение/запись из регистра. Причем, в этом условии
  97. части регистров считаются за весь регистр (то есть, запись в al/ah
  98. расценивается как запись в eax, а запись в cf - как запись в flags).
  99. Пример:
  100. <pre class=source>
  101. mov eax,1234h / mov ebx,eax - НЕ будут спарены
  102. mov eax,1234h / mov ebx,1234h - будут спарены
  103. inc eax / mov ecx,eax - НЕ будут спарены
  104. mov ecx,eax / inc ecx - будут спарены
  105. mov al,bl / mov ah,0 - НЕ будут спарены
  106. </pre>
  107. <li>Две команды, записывающие что-то в регистр флагов, могут быть
  108. спарены, несмотря на условие 2:
  109. <pre class=source>shr ebx,4 / inc ebx - спарится</pre>
  110. <li>Команда, записывающая что-то в регистр флагов, может быть спарена
  111. с условным переходом, несмотря на условие 2:
  112. <pre class=source>cmp eax,2 / ja @@label_bigger - спарится</pre>
  113. <li>Следующие пары команд могут спариться несмотря на то, что обе команды
  114. изменяют esp:
  115. <pre class=source>push + push, push + call, pop + pop</pre>
  116. <li>Существуют ограничения на исполнение команд с префиксом. Префиксы
  117. возникают в таких случаях:
  118. <ul>
  119. <li>команда, адресующаяся не к сегменту по умолчанию, имеет префикс
  120. сегмента (примеры: mov eax,es:[ebx]; mov eax,ds:[ebp])
  121. <li>команда, работающая с 16-битными операндами в 32-битном режиме
  122. или с 32-битными операндами в 16-битном режиме, имеет префикс
  123. разрядности операнда (примеры: mov ax,1234 в защищенном режиме;
  124. mov ax,word ptr [variable] в защищенном режиме; xor eax,eax в
  125. реальном режиме)
  126. <li>команды, использующая 32-битную адресацию в 16-битном режиме,
  127. имеет префикс разрядности адреса (пример: mov ax,[ebx] в реальном
  128. режиме)
  129. <li>rep, lock - префиксы (пример: rep stosd)
  130. <li>многие команды, которых не было на 8086, имеют двухбайтовый код
  131. команды, где первый байт равен 0Fh. На процессоре Pentium без MMX
  132. этот байт считается префиксом. Наиболее часто встречающиеся команды
  133. с префиксом 0Fh: movzx, movsx, push/pop fs/gs, lfs/lgs/lss, setXX,
  134. bt/btc/btr/bts/bsf/bsr/shld/shrd, imul с двумя операндами и без
  135. операнда-числа (immediate).
  136. </ul>
  137. <br>На процессоре Pentium без MMX команда с префиксом может исполняться
  138. только в U-pipe, исключение - близкие переходы по условию (conditional
  139. near jumps). На процессоре Pentium с MMX команды с префиксами 0Fh и
  140. размера операнда или адреса может исполняться в любом конвейере; но
  141. команды с префиксами сегмента, rep или lock (повторения или блокировки
  142. шины) могут исполняться только в U-pipe.<br><br>
  143. <li>Команда, в которой одновременно участвует смещение (displacement) и
  144. заданное число (immediate) не может быть спарена на процессоре Pentium
  145. без MMX и может быть выполнена и спарена только в U-pipe на процессоре
  146. Pentium с MMX. Вот примеры:
  147. <pre class=source>
  148. mov byte ptr ds:[1000],0 ; НЕ спаривается ни с чем
  149. mov byte ptr [ebx+8],1 ; НЕ спаривается ни с чем
  150. mov byte ptr [ebx],1 ; спаривается в U-pipe
  151. mov byte ptr [ebx+8],al ; спаривается в U-pipe
  152. </pre>
  153. </ol>
  154. <p>Спаривающаяся команда, которая читает из памяти, считает и записывает
  155. результат в регистр или в регистр флагов занимает 2 такта. Спаривающаяся
  156. команда, которая читает из памяти, считает и записывает результат обратно
  157. в память занимает 3 такта. Примеры таких команд:
  158. <pre class=source>
  159. add eax,[ebx] ; 2 такта
  160. add [ebx],eax ; 3 такта
  161. </pre>
  162. <p>Существует также так называемое неполное спаривание (imperfect pairing),
  163. когда обе команды выполняются в разных конвейерах, но НЕ одновременно
  164. (возможно, частично перекрываясь по времени исполнения), а следующие за
  165. ними команды не могут начать исполнение, пока обе команды не закончатся.
  166. Такое случается в следующих случаях:
  167. <ol>
  168. <li>Вторая команда вызывает AGI (address generation interlock,
  169. блокировка генерирования адреса). Это происходит, если адрес,
  170. используемый во второй команде зависит от регистров, измененных
  171. в первой команде. Примеры:
  172. <pre class=source>
  173. add ebx,4 / mov eax,[ebx] ; AGI
  174. mov eax,[ebx+4] / add ebx,4 ; нормально спаривается
  175. add esp,4 / pop esi ; AGI (pop использует esp)
  176. inc esi / lea eax,[ebx+4*esi] ; AGI
  177. </pre>
  178. <li>Две команды одновременно обращаются к одному и тому же двойному
  179. слову памяти. Примеры (подразумевается, что esi делится на 4):
  180. <pre class=source>
  181. mov al,[esi] / mov bl,[esi+1] ; неполное спаривание
  182. mov al,[esi+3] / mov bl,[esi+4] ; нормальное спариваение
  183. </pre>
  184. <li>Две команды одновременно обращаются к адресам, в которых одинковы
  185. биты 2-4 (это вызывает конфликт кэш-банков). Для dword-адресов это
  186. значит, что разница между двумя адресами делится на 32. Пример:
  187. <pre class=source>
  188. mov eax,[esi] / mov ebx,[esi+32000] ; неполное спаривание
  189. mov eax,[esi] / mov ebx,[esi+32004] ; нормальное спаривание
  190. </pre>
  191. <li>Первая команда производит чтение, подсчет и запись одновременно;
  192. вторая - чтение и изменение; в этом случае число тактов, требующееся
  193. для выполнения пары команд, можно рассчитать по следующей таблице:
  194. <p>
  195. <table cellpadding=0 cellspacing=0 border=0 align=center><td bgcolor=#5E5EA5>
  196. <table cellspacing=1 cellpadding=4 border=0 align=center>
  197. <tr><td bgcolor=white>&nbsp;</td><td colspan=3 align=center bgcolor=white>первая команда</td></tr>
  198. <tr>
  199. <td align=center bgcolor=white>вторая команда</td>
  200. <td align=center bgcolor=white>mov или<br>регистровая</td>
  201. <td align=center bgcolor=white>чтение/<br>подсчет</td>
  202. <td align=center bgcolor=white>чтение/подсчет/<br>запись</td>
  203. </tr>
  204. <tr><td bgcolor=white>mov или регистровая</td><td align=center bgcolor=white>1</td><td align=center bgcolor=white>2</td><td align=center bgcolor=white>3</td></tr>
  205. <tr><td bgcolor=white>чтение/подсчет</td><td align=center bgcolor=white>2</td><td align=center bgcolor=white>2</td><td align=center bgcolor=white>4</td></tr>
  206. <tr><td bgcolor=white>чтение/подсчет/запись</td><td align=center bgcolor=white>3</td><td align=center bgcolor=white>3</td><td align=center bgcolor=white>5</td></tr>
  207. </table>
  208. </td></table>
  209. <p>Примеры:
  210. <pre class=source>
  211. add [mem1],eax / add ebx,[mem2] ; 4 такта
  212. add ebx,[mem2] / add [mem1],eax ; 3 такта
  213. add [mem1],eax / add [mem2],ebx ; 5 тактов
  214. add [mem1],eax / sub ebx,ecx ; 3 такта
  215. </pre>
  216. </ol>
  217. <div align=left>
  218. <a name="612"></a>
  219. <p class=title><img src="../img/b6.gif" width=70 height=70 align=left hspace=0 alt=""><img src="../img/t.gif" width=5 height=70 align=left hspace=0 alt="">6.1.2. Кэш-память
  220. <div align=justify>
  221. <p>У процессора Pentium непосредственно на кристалле есть 8k кэш-памяти (это
  222. т.н. кэш-память первого уровня, L1 cache) для кода и 8k - для данных. Данные
  223. из L1 cache считываются/записываются за один такт; кэш-промах же может
  224. стоить довольно много тактов. Таким образом, для наиболее эффективного
  225. использования кэша необходимо знать, как он работает.
  226. <p>Итак, L1 cache состоит из 256 кэш-линий (cachelines), по 32 байта в каждой.
  227. При чтении данных, которых нет в кэше, процессор считывает из памяти целую
  228. кэш-линию. Кэш-линии всегда выравнены на физический адрес, делящийся на 32;
  229. так что если прочитать байт по адресу, делящемуся на 32, то можно читать и
  230. писать в следующий за ним 31 байт без всяких задержек. Свои данные имеет
  231. смысл располагать с учетом этого факта - например, выравнивать массивы из
  232. структур длиной 32 байта на 32; перед записью в видеопамять читать оттуда
  233. один байт (один раз на 32 записываемых байта); используемые вместе данные
  234. располагать вместе; и так далее.
  235. <p>Но кэш-линия не может быть связана с любым физическим адресом. У каждой
  236. кэш-линии есть некое 7-битное "заданное значение" (set-value), которое
  237. должно совпадать с битами адреса 5-11. Для каждого из 128 возможных значений
  238. set-value есть две кэш-линии. Отсюда следует то, что в кэше не может
  239. одновременно содержаться более двух блоков данных с одинаковыми битами
  240. адреса 5-11. Чем это чревато, покажем на примере.
  241. <pre class=source>
  242. ; пусть в esi - адрес, делящийся на 32
  243. loop_label:
  244. mov eax,[esi]
  245. mov ebx,[esi+13*4096+4]
  246. mov ecx,[esi+20*4096+28]
  247. dec edx
  248. jnz loop_label
  249. </pre>
  250. <p>У используемых трех адресов будет одинаковое значение в битах 5-11. Поэтому
  251. к моменту самого первого чтения в ecx в кэше точно не окажется свободной
  252. кэш-линии, процессор выберет для нее наименее использованную (least recently
  253. used) линию, ту самую, которая была использована при чтении eax. При
  254. чтении ebx, соответственно, будет заново перекрыта линия, использованная
  255. при чтении ecx... В результате цикл будет состоять из сплошных кэш-промахов
  256. и съест порядка 60 тактов. Если же поменять 28 на 32, изменив, таким образом,
  257. на единичку биты 5-11 для адреса [esi+20*4096+28], то для чтения в eax и ebx
  258. будут как раз использованы две имеющихся линии, для чтения в ecx - третья,
  259. не совпадающая ни с одной из этих двух. В результате - скорость порядка
  260. трех тактов на один проход и ускорение примерно в 20 (!!!) раз.
  261. <p>Еще одна интересная вещь, которую стоит учесть - Pentium НЕ загружает
  262. кэш-линию при промахе записи; только при промахе чтения. При промахе записи
  263. данные пойдут в L2 cache или память (в зависимости от настроек L2 cache).
  264. А это довольно медленно. Поэтому, если мы последовательно пишем в один и
  265. тот же 32-байтовый блок, но не читаем оттуда, то имеет смысл сначала сделать
  266. холостое чтение из этого блока, чтобы загрузить его в L1 cache; тогда все
  267. последовательные операции записи будут есть только по одному такту.
  268. <div align=left>
  269. <a name="613"></a>
  270. <p class=title><img src="../img/b6.gif" width=70 height=70 align=left hspace=0 alt=""><img src="../img/t.gif" width=5 height=70 align=left hspace=0 alt="">6.1.3. Разные трюки
  271. <div align=justify>
  272. <p>Трюков есть много, перечислим здесь только наиболее часто используемые:
  273. <ul>
  274. <li><p>работа с fixed point вместо floating point иногда (если код не
  275. слишком сильно насыщен математикой) быстрее; практически всегда
  276. быстрее для клонов;
  277. <li><p>все данные желательно выравнивать по адресам, кратным размеру данных
  278. (то есть, переменные-байты можно не выравнивать, слова - выравнивать
  279. на 2, двойные слова - на 4); обращение к невыравненной переменной
  280. влечет за собой задержку минимум на три такта;
  281. <li><p>деление на заранее известное число можно заменить умножением на
  282. обратное ему число;
  283. <li><p>деление на степень двойки для целых чисел заменяется на сдвиг влево;
  284. деление чисел с плавающей точкой (fdiv) на Intel Pentium (на клонах,
  285. к несчастью, это не так) может исполняться параллельно с целочисленными
  286. командами.
  287. </ul>
  288. </div>
  289. </td></table>
  290. <!-- Bottom Navigation -->
  291. <img src="../img/b.gif" width=500 height=1 alt=""><br><img src="../img/t.gif" width=500 height=2 alt=""><br>
  292. <table width=500 cellpadding=0 cellspacing=0 border=0>
  293. <td><img src="../img/t.gif" width=5 height=1 alt=""><a href="../main.htm" onmouseover="swap('logo2','../img/xe.gif');" onmouseout="swap('logo2','../img/e.gif');"><img src="../img/e.gif" name=logo2 width=60 height=50 hspace=10 border=0 alt=" в самое начало "></a></td>
  294. <td><p class=pagetitle><img src="../img/t.gif" width=265 height=1 alt=""><br>demo.design<br>3D programming FAQ</td>
  295. <td align=center><p class=navy><a href="56.htm" onmouseover="swap('prev','../img/xprev.gif');" onmouseout="swap('prev','../img/prev.gif');"><img src="../img/prev.gif" name=prev width=40 height=40 border=0 hspace=5 alt=" предыдущая статья "></a><br>previous</td>
  296. <td align=center><p class=navy><a href="../content.htm" onmouseover="swap('faq','../img/xfaq.gif');" onmouseout="swap('faq','../img/faq.gif');"><img src="../img/faq.gif" name=faq width=40 height=40 border=0 hspace=5 alt=" содержание "></a><br>content</td>
  297. <td align=center><p class=navy><a href="62.htm" onmouseover="swap('next','../img/xnext.gif');" onmouseout="swap('next','../img/next.gif');"><img src="../img/next.gif" name=next width=40 height=40 border=0 hspace=5 alt=" следующая статья "></a><br>next</td>
  298. </table>
  299. <img src="../img/t.gif" width=500 height=4 alt=""><br>
  300. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  301. <img src="../img/t.gif" width=500 height=1 alt=""><br>
  302. <img src="../img/b.gif" width=500 height=1 alt=""><br>
  303. </center></body>
  304. </html>