<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title><![CDATA[Серый форум &mdash; AHK: Ускорение скриптов машинным кодом]]></title>
		<link>http://forum.script-coding.com/viewtopic.php?id=3465</link>
		<atom:link href="http://forum.script-coding.com/extern.php?action=feed&amp;tid=3465&amp;type=rss" rel="self" type="application/rss+xml" />
		<description><![CDATA[Недавние сообщения в теме «AHK: Ускорение скриптов машинным кодом».]]></description>
		<lastBuildDate>Thu, 30 Jul 2009 08:12:13 +0000</lastBuildDate>
		<generator>PunBB</generator>
		<item>
			<title><![CDATA[AHK: Ускорение скриптов машинным кодом]]></title>
			<link>http://forum.script-coding.com/viewtopic.php?pid=26592#p26592</link>
			<description><![CDATA[<p>Идея взята из темы <a href="http://www.autohotkey.com/forum/topic21172.html">Machine code functions: Bit Wizardry</a>. Суть в том, что начиная с версии 1.0.46.08 в DllCall можно вместо имени функции указывать её адрес. Таким образом, если вырезать из какого-либо EXE- или DLL-файла код какой-либо функции и поместить в переменную в скрипте, то эту функцию можно потом вызывать через DllCall и она будет прекрасно работать — при условии, что в ней отсутствуют внешние ссылки, т.е. вызовы каких-то других функций или обращения к внешним данным. Функция должна взаимодействовать с внешним миром только через свои аргументы и возвращаемое значение.</p><p>Эффект от применения машинного кода может быть очень велик в случаях, когда нужно обрабатывать большой объём данных. Для примера можно взять задачу, обсуждавшуюся на форуме: <a href="http://www.forum.script-coding.com/viewtopic.php?id=3442">AHK: вычисление простых чисел</a>. При поиске в диапазоне до миллиона скрипто-машинный код, приведённый ниже, быстрее чисто скриптового в 160 раз, дальше разница ещё больше. Машинная функция написана мной на ассемблере и скомпилирована при помощи <a href="http://www.jorgon.freeserve.co.uk/index.htm">The Go tools for Windows + Assembler</a>.<br /></p><div class="codebox"><pre><code>#MaxMem, 256    ; Нужно, например, если Limit = 100.000.000

Limit = 1000000    ; Проверять от 1 до этого числа включительно.
OutFile = %A_Desktop%\MyPrimes.txt

IfExist, %OutFile%
    FileDelete, %OutFile%

        time0 := A_TickCount    ; Замер времени, в научных целях.

; Вызов скриптовой функции - оболочки машинного кода (см. ниже).
; Возвращает количество найденных простых чисел.
Found := PrimeErat(Buf, Limit, 1) ; 1 - конвертировать в строку, 0 - нет.

        time1 := A_TickCount - time0

FileAppend, %Buf%, %OutFile%  ; Так сохранится, только если в виде строки.

        time2 := A_TickCount - time0 - time1
        time := A_TickCount - time0

; Точки между тройками цифр в MsgBox ниже — для разборчивости.
strLimit := RegExReplace(Limit, &quot;(?&lt;=\d)(\d\d\d)(?=\d\d\d$|$)&quot;, &quot;.$1&quot;)
strFound := RegExReplace(Found, &quot;(?&lt;=\d)(\d\d\d)(?=\d\d\d$|$)&quot;, &quot;.$1&quot;)

MsgBox, 
(
Проверено: %strLimit%
Найдено: %strFound%

Поиск и в строку: %time1% мс
В файл: %time2% мс
Общее время: %time% мс

Сохранено:
%OutFile%
)

; ================= Функция поиска ===================

PrimeErat(ByRef Buf, Limit, ToString)
{
    Hex =        ; Собственно машинный код, в HEX-виде.
    (Join LTrim 
    5553575689E583EC0C8B5D148B4D1831C083F9020F8CCE0000008D344B89D983
    7D1C00740E0FBAEE0066C701322083C102EB09C7010200000083C104894DF489
    DFC745FC01000000FC83C70439F70F87850000008B0785C078EFFF45FC89F829
    D8D1E8400FBAE600732F505357568B7DF4BB0A00000031C989E64EC606204131
    D2F7F380C2304E88164185C075F1F3A4897DF45E5F5B58EB0B8B4DF4890183C1
    04894DF489C1F7E0729F3B4518779A48D1E001D8BAFFFFFFFF891089C789C8C1
    E00201C739F777048917EBF6D1E883E8028D3C03E970FFFFFF8B45FC0FBAE600
    73068B55F4C6020089EC5E5F5B5DC20C00
    )
    VarSetCapacity(Code, Len := (StrLen(Hex) // 2)) ; Переменная под код.
    Char := Offset := -1
    Loop, % Len            ; Запись кода функции в переменную.
        NumPut(&quot;0x&quot; . SubStr(Hex, Char += 2, 2), Code, ++Offset, &quot;Char&quot;)
    VarSetCapacity(Buf, (Limit+1) * 2, 0)    ; Под найденные простые числа.
    ; Вызов машинной функции. Вернёт количество найденных простых.
    ; Первый аргумент DllCall — адрес переменной, куда был записан код.
    Found := DllCall(&amp;Code, &quot;uint&quot;, &amp;Buf, &quot;uint&quot;, Limit, &quot;int&quot;, ToString)
    ; Если была конверсия в строку, нужно обновить переменную, иначе
    ; она нормально не отобразится и не запишется в файл.
    If ToString
        VarSetCapacity(Buf, -1)

    Return Found
}</code></pre></div><p>Исходник машинного кода:<br /></p><div class="codebox"><pre><code>CODE SECTION

Start FRAME pArray, uLimit, iStr
      USES ebx,edi,esi
      LOCALS uCount, uNum, pResCur

    mov ebx,[pArray]
    mov ecx,[uLimit]
    xor eax,eax        ; Found = 0, если uLimit &lt; 2.
    cmp ecx,2
    jl &gt;&gt;.exit
        lea esi,[ebx+ecx*2]
        mov ecx,ebx
        cmp D[iStr],0
        je &gt;
            bts esi,0
            mov W[ecx],2032h    ; 2 и пробел.
            add ecx,2
            jmp &gt; 1
        :
        mov D[ecx],2
        add ecx,4
            1:
        mov [pResCur],ecx
        mov edi,ebx
        mov D[uCount],1
        cld
.find_next
    add edi,4
    cmp edi,esi
    ja &gt;&gt;.done
        mov eax,[edi]
        test eax,eax
        js .find_next
            inc D[uCount]
            mov eax,edi
            sub eax,ebx
            shr eax,1
            inc eax
            bt esi,0
            jnc &gt;
                push eax,ebx,edi,esi
                mov edi,[pResCur]
                mov ebx,10
                xor ecx,ecx
                mov esi,esp
                dec esi
                mov B[esi],&#039; &#039;
                inc ecx
                1:
                    xor edx,edx
                    div ebx
                    add dl,30h
                    dec esi
                    mov [esi],dl
                    inc ecx
                    test eax,eax
                jnz &lt; 1
                rep movsb
                mov [pResCur],edi
                pop esi,edi,ebx,eax
                jmp &gt; 1
            :
            mov ecx,[pResCur]
            mov [ecx],eax
            add ecx,4
            mov [pResCur],ecx
                1:
            mov ecx,eax
            mul eax
            jc .find_next
                cmp eax,[uLimit]
                ja .find_next
                    dec eax
                    shl eax,1
                    add eax,ebx
                    mov edx,-1
                    mov [eax],edx
                    mov edi,eax
                    mov eax,ecx
                    shl eax,2
.mark_out
    add edi,eax
    cmp edi,esi
    ja &gt;
        mov [edi],edx
        jmp .mark_out
    :
    shr eax,1
    sub eax,2
    lea edi,[ebx+eax]
    jmp .find_next
.done
    mov eax,[uCount]
    bt esi,0
    jnc &gt;.exit
        mov edx,[pResCur]
        mov B[edx],0
.exit
    ret

ENDF</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (YMP)]]></author>
			<pubDate>Thu, 30 Jul 2009 08:12:13 +0000</pubDate>
			<guid>http://forum.script-coding.com/viewtopic.php?pid=26592#p26592</guid>
		</item>
	</channel>
</rss>
