{"id":69,"date":"2008-12-07T00:43:39","date_gmt":"2008-12-06T23:43:39","guid":{"rendered":"http:\/\/netrix.org.pl\/index.php\/2008\/12\/07\/niskopoziomowa-zabawa\/"},"modified":"2011-02-12T02:55:49","modified_gmt":"2011-02-12T01:55:49","slug":"niskopoziomowa-zabawa","status":"publish","type":"post","link":"http:\/\/netrix.org.pl\/index.php\/2008\/12\/07\/niskopoziomowa-zabawa\/","title":{"rendered":"Niskopoziomowa zabawa"},"content":{"rendered":"<p>Zabawa w kodzie na niskim poziomie mo\u017ce by\u0107 bardzo fajna, ja por\u00f3wnuj\u0119 j\u0105 do rozwi\u0105zywania \u0142amig\u0142\u00f3wek (my\u015bl\u0119, \u017ce s\u0105 osoby, kt\u00f3re podzielaj\u0105 moje zdanie :)). Czasem ta zabawa sprowadza si\u0119 zbadania, czy zmiana jednej funkcji assemblerowej powoduje przyspieszenie kodu o ten jeden cykl, chocia\u017c jest to ju\u017c skrajno\u015b\u0107. Przyk\u0142adem tego mo\u017ce by\u0107 funkcja obliczaj\u0105ca d\u0142ugo\u015b\u0107 wektora 4D wykorzystuj\u0105ca funkcje wewn\u0119trzne kompilatora &#8211; <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/26td21ds(VS.80).aspx\">Intrinsics<\/a>:<\/p>\n<pre lang=\"cpp\">float Length()\r\n{\r\n\u00a0  float f;\r\n\u00a0  __m128 temp = _mm_mul_ps(m_vector, m_vector);\r\n\u00a0  __m128 temp2 = _mm_add_ps(_mm_movehl_ps(temp, temp), temp);\r\n\u00a0  temp = _mm_shuffle_ps(temp2, temp2, _MM_SHUFFLE(0,0,0,1));\r\n\u00a0  _mm_store_ss(&f, _mm_sqrt_ss(_mm_add_ss(temp2, temp)));\r\n\u00a0  return f;\r\n}<\/pre>\n<p>Powy\u017cszy kod jest najszybsz\u0105 wersj\u0105 jak\u0105 uda\u0142o mi si\u0119 uzyska\u0107. Polega on na obliczeniu kwadrat\u00f3w wszystkich sk\u0142adowych, dodaniu ich do siebie oraz zpierwiastkowanie. Najbardziej pracoch\u0142onn\u0105 cz\u0119\u015bci\u0105 jest tutaj wbrew pozorom dodawanie, gdy\u017c kwadrat sk\u0142adowych mo\u017cna za\u0142atwi\u0107 jednym mno\u017ceniem wektorowym. Dodawanie jednak rozbija si\u0119 na dwie operacje dodawania oraz dwie operacje przemieszania, poniewa\u017c SSE nie oferuje funkcji, kt\u00f3ra dodawa\u0142aby wszystkie elementy pojedynczego rejestru. Do wykonywania operacji przemieszania najcz\u0119\u015bciej stosuje si\u0119 funkcje _mm_shuffle(), poniewa\u017c pozwala ona dowolnie rozmie\u015bci\u0107 elementy w rejestrze. Rozszerzenie SSE umo\u017cliwia jednak wykonanie przemiesza\u0144 za pomoc\u0105 innych funkcji (<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/skccxx7d%28VS.80%29.aspx\" class=\"tocSelected\" target=\"_top\">_mm_unpackhi_ps()<\/a>, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/25st103b%28VS.80%29.aspx\" target=\"_top\">_mm_unpacklo_ps()<\/a>, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/6h6ddxdw%28VS.80%29.aspx\" target=\"_top\">_mm_movehl_ps()<\/a>, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/946dzczz%28VS.80%29.aspx\" target=\"_top\">_mm_movelh_ps()<\/a>), kt\u00f3re mieszaj\u0105 elementy tylko w okre\u015blony spos\u00f3b. W powy\u017cszym kodzie zastosowa\u0142em w\u0142a\u015bnie _mm_unpackhi(), poniewa\u017c funkcja zapisuje elementy x i y na pozycji elementow z i w.<\/p>\n<p>Inn\u0105 spraw\u0105 jest, w jaki spos\u00f3b kompilator radzi sobie z takim kodem. Ot\u00f3\u017c posiada on specjalnie zaprogramowane optymalizacje dla tego typu funkcji oraz typu __m128. Kompilator widz\u0105c te funkcje zamienia je na wywo\u0142ania odpowiednich funkcji w kodzie assemblera, przy czym sam zarz\u0105dza wykorzystaniem rejestr\u00f3w xmm. Nie trzeba si\u0119 zatem martwi\u0107 o ilo\u015b\u0107 zmiennych typu __m128, poniewa\u017c nie wp\u0142ywa to bezpo\u015brednio na rejestry.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Zabawa w kodzie na niskim poziomie mo\u017ce by\u0107 bardzo fajna, ja por\u00f3wnuj\u0119 j\u0105 do rozwi\u0105zywania \u0142amig\u0142\u00f3wek (my\u015bl\u0119, \u017ce s\u0105 osoby, kt\u00f3re podzielaj\u0105 moje zdanie :)). Czasem ta zabawa sprowadza si\u0119 zbadania, czy zmiana jednej funkcji assemblerowej powoduje przyspieszenie kodu o ten jeden cykl, chocia\u017c jest to ju\u017c skrajno\u015b\u0107. Przyk\u0142adem tego mo\u017ce by\u0107 funkcja obliczaj\u0105ca d\u0142ugo\u015b\u0107 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[179,84,22],"_links":{"self":[{"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/69"}],"collection":[{"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/comments?post=69"}],"version-history":[{"count":2,"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/69\/revisions"}],"predecessor-version":[{"id":816,"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/69\/revisions\/816"}],"wp:attachment":[{"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/media?parent=69"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/categories?post=69"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/tags?post=69"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}