2013-09-27 32 views
8

Aşağıdaki kod:Delphi nop'ları niçin ortasına sokar?

while Assigned(p) do begin 
    np:= p^.next; 
    h:= leaf_hash(p^.data); <<-- inline routine 
    h:= h mod nhashprime; 
    p^.next:= nhashtab[h]; 
    nhashtab[h]:= p; 
    p:= np; 
end; { while } 

şu montaj üretir:

hlife.pas.605: h:= leaf_hash(p^.data); 
00000000005D4602 498B4018   mov rax,[r8+$18] 
00000000005D4606 48C1E830   shr rax,$30 
00000000005D460A 498B5018   mov rdx,[r8+$18] 
00000000005D460E 48C1EA20   shr rdx,$20 
00000000005D4612 81E2FFFF0000  and edx,$0000ffff 
00000000005D4618 4D8B5818   mov r11,[r8+$18] 
00000000005D461C 49C1EB10   shr r11,$10 
00000000005D4620 4181E3FFFF0000 and r11d,$0000ffff 
00000000005D4627 418B7018   mov esi,[r8+$18] 
00000000005D462B 81E6FFFF0000  and esi,$0000ffff 
00000000005D4631 488D34F6   lea rsi,[rsi+rsi*8] 
00000000005D4635 4403DE   add r11d,esi 
00000000005D4638 4F8D1CDB   lea r11,[r11+r11*8] 
00000000005D463C 4103D3   add edx,r11d 
00000000005D463F 488D14D2   lea rdx,[rdx+rdx*8] 
00000000005D4643 03C2    add eax,edx 
hlife.pas.606: h:= h mod nhashprime; 
00000000005D4645 8BC0    mov eax,eax <<--- Why is there a NOP here? 
00000000005D4647 4C63DB   movsxd r11,rbx 
00000000005D464A 4899    cwd 
00000000005D464C 49F7FB   idiv r11 
00000000005D464F 488BC2   mov rax,rdx 
hlife.pas.607: p^.next:= nhashtab[h]; 
00000000005D4652 488B5538   mov rdx,[rbp+$38] 

Delphi nhashtab[h]:= p; hattı önce NOP ekler. leaf_hash işlevi normal bir işlev olsaydı bu anlamlı olurdu.
(hayır değil RET hala nop yürütme [5D4645] döneceğini gerçekten çünkü)
Ama şimdi bu bir atlama hedef değildir.

Yani ben (sadece), Neden bu meraklı yapar?

[DÜZENLEME]: SSCCE Ben {çok kısa değil ama yapmak zorunda olacak bir SSCCE kadar var Tamam
.

Not Bu sökülmesini göreceksiniz

enter image description here

unit Unit16; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; 

type 
    pnode = ^node; 
    tflavour = (tnode, tleaf, tleaf64); 

    node = record 
    case flavour: tflavour of 
     tnode: (next: pnode; (* hash link *) 
      nw, ne, sw, se: pnode; (* constant; nw not= 0 means nonleaf *) 
      res: pnode); (* cache *) 
     tleaf: (next1: pnode; (* hash link *) 
      isnode: pnode; (* must always be zero for leaves *) 
      nw1, ne1, sw1, se1: word; (* constant *) 
      res1, res2: word; (* constant *) 
     ); 
     tleaf64: (next2: pnode; (* hash link *) 
      isnode1: pnode; (* must always be zero for leaves *) 
      data: Uint64; (* constant *) 
      res1a, res2a: word; (* constant *) 
     ) 
    end; 

    ppnode = array of pnode; 

    THashBox = class(TPersistent) 
    strict private 
    leafhashpop: integer; 
    leafhashlimit: integer; 
    leafhashprime: integer; 
    leafhashtab: ppnode; 
    nodehashpop: integer; 
    nodehashlimit: integer; 
    nodehashprime: integer; 
    nodehashtab: ppnode; 
    private 
    TotalTime, Occurrences: Uint64; 
    StartTime, EndTime: Uint64; 
    procedure resize_leaves(); 
    public 
    constructor Create; 
    end; 

    TForm16 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    private 
    HashBox: THashBox; 
    public 
    end; 

var 
    Form16: TForm16; 

implementation 

{$R *.dfm} 

const 
    maxmem = 2000*1000*1000; {2GB} 

var 
    alloced: cardinal; 

function rdtsc: int64; assembler; 
asm 
    { xor eax,eax; 
    push rbx 
    cpuid 
    pop rbx } 
    rdtsc 
end; 

function node_hash(n: pnode): cardinal; { inline; } assembler; overload; 
// var 
// a: pnativeint; 
    // begin 
    // Result:= nativeint(n^.se) + 3 * (nativeint(n^.sw) + 3 * (nativeint(n^.ne) + 3 * nativeint(n^.nw) + 3)); 
asm 
    mov eax,[rcx+node.nw] 
    lea eax,[eax+eax*2+3] 
    add eax,[rcx+node.ne] 
    lea eax,[eax+eax*2] 
    add eax,[rcx+node.sw] 
    lea eax,[eax+eax*2] 
    add eax,[rcx+node.se] 
end; 

function leaf_hash(a, b, c, d: cardinal): cardinal; inline; overload; 
begin 
    Result:= (d + 9 * (c + 9 * (b + 9 * a))) 
end; 

function leaf_hash(data: Uint64): cardinal; inline; overload; 
begin 
    // Result:= d + 9 * (c + 9 * (b + 9 * a)); 
    Result:= ((data shr 48) + 9 * (((data shr 32) and $FFFF) + 9 * (((data shr 16) and $FFFF) + 9 * (data and $FFFF)))); 
    Inc(Result); 
end; 

procedure TForm16.Button1Click(Sender: TObject); 
begin 
    HashBox:= THashBox.Create; 
    Hashbox.resize_leaves; 
end; 

function nextprime(old: integer): integer; 
begin 
    Result:= 1009; 
end; 

constructor THashBox.Create; 
begin 
    leafhashprime:= 7; 
    SetLength(leafhashtab, leafhashprime); 
end; 

procedure THashBox.resize_leaves(); 
var 
    i, i1, i2: integer; 
    nhashprime: Cardinal; 
    p: pnode; 
    nhashtab: ppnode; 
    np: pnode; 
    h: Integer; 
    n, n2: integer; 
    diff1, diff2: integer; 
begin 
    nhashprime:= nextprime(4 * leafhashprime); 
    if (nhashprime * sizeof(pnode) > maxmem - alloced) then begin 
    leafhashlimit:= 2000 * 1000 * 1000; 
    exit; 
    end; 
    (* 
    * Don't let the hash table buckets take more than 4% of the 
    * memory. If we're starting to strain memory, let the buckets 
    * fill up a bit more. 
    *) 
    if (nhashprime > maxmem div 100) then begin 
    nhashprime:= nextprime(maxmem div 100); 
    if (nhashprime = leafhashprime) then begin 
     leafhashlimit:= 2000 * 1000 * 1000; 
     exit; 
    end; 
    end; 
    SetLength(nhashtab, nhashprime); //make a new table, do not resize the existing one. 
    alloced:= alloced + sizeof(pnode) * (nhashprime - leafhashprime); 

    diff1:= maxint; 
    for i1:= 0 to 100 do begin 
    n:= 0; 
    StartTime:= rdtsc; 
    for i:= 0 to leafhashprime - 1 do begin 
     p:= leafhashtab[i]; 
     if Assigned(p) then begin 
     h:= node_hash(p); 
     h:= h mod nhashprime; 
     inc(n, h); 
     end; 
    end; 
    EndTime:= rdtsc; 
    if ((EndTime - StartTime) < diff1) then diff1:= (EndTime - StartTime); 

    end; 

    diff2:= maxint; 
    for i1:= 0 to 100 do begin 
    n2:= 0; 
    StartTime:= rdtsc; 
    for i:= 0 to leafhashprime - 1 do begin 
     p:= leafhashtab[i]; 
     if Assigned(p) then begin 
     inc(n2); 
     end; 
    end; 
    EndTime:= rdtsc; 
    if (endtime - starttime) < diff2 then diff2:= endtime - starttime; 
    end; 

    TotalTime:= diff1 - diff2; 
    if n <> n2 then Occurrences:= nhashprime; 

    for i:= 0 to leafhashprime - 1 do begin 
    // for (p=hashtab[i]; p;) { 
    p:= leafhashtab[i]; 
    while Assigned(p) do begin <<--- put a breakpoint here 
     np:= p^.next; 
     h:= leaf_hash(p^.data); 
     h:= h mod nhashprime; 
     p^.next:= nhashtab[h]; 
     nhashtab[h]:= p; 
     p:= np; 
    end; { while } 
    end; { for i } 
    // free(hashtab); 
    leafhashtab:= nhashtab; 
    leafhashprime:= nhashprime; 
    leafhashlimit:= leafhashprime; 
end; 

end. 

derleyici ayarları (hata ayıklama + Win64):

Özellikle döngüler içinde, kod hizalanması için bir optimizasyon olduğunu
Unit16.pas.196: h:= h mod nhashprime; 
000000000059CE4B 4863C0   movsxd rax,rax 
000000000059CE4E 448B5528   mov r10d,[rbp+$28] 
000000000059CE52 458BD2   mov r10d,r10d  <<--- weird NOP here 
000000000059CE55 4899    cwd 
000000000059CE57 49F7FA   idiv r10 
000000000059CE5A 488BC2   mov rax,rdx 
Unit16.pas.197: p^.next:= nhashtab[h]; 
000000000059CE5D 488B5538   mov rdx,[rbp+$38] 
+0

Muhtemelen hata ayıklama için tahmin edeceğim? Dürüst olmak gerekirse –

+1

no'lu ipucum En iyi tahminim, belirli bir sınırda kod baytlarını hizalamak için bir çeşit optimizasyon olurdu. – Glenn1234

+3

Bu sorunun ciddi şekilde SSCCE'ye ihtiyacı var. Birisi olmadan derleyiciyi inceleyemeyiz. Bunu yapmak istiyorum ve derleyicinin farklı sürümleriyle. Ama ben yapamam. –

cevap

5

cevap

MOV EAX,EAX 

üst 32 bit sıfır olacak 64 bit kayıt alt 32 bit manipüle x64 üzerinde bir no-op

değil mi olduğudur.32 bit operasyonların

Sonuçları örtülü 64 bit değerlerine genişletilmiş sıfır AMD

göre

MOVZX RAX,EAX 

:
Yani yukarıdaki talimat gerçekten olarak okunmalı.

+0

+1 x86/x64 farkını bile düşünmemişti –

0

, önbellek hattı duraklarını ve benzeri önlemek için.

+1

Bu hizalama NOP'larının, ortada bir yerde değil, döngülerinin sonunda ya da başlangıcında olmasını beklemem. Bir şube hedefinin olmadığı ortada yerleştirmenin amacı nedir? – Johan

+1

Bunun optimizasyonda nasıl sonuçlandığını açıklayacak mısınız? Çünkü emin değil gibi görünüyor. –

+1

David ile aynı fikirdeyim. Hizalama, döngü etiketinden önce 4/8 bayt sınırına hizalanacak şekilde yapılır. Burada hizalama yok, sadece yanlış optimizasyon var. –

4

IMHO bu, hizalama için bir nop değil, ancak benim için optimize edilmemiş üretilen kod gibi ve kendi değişkenlerinizin yanlış imzalanması gibi geliyor.

h:= h mod nhashprime; 

bölünebilir olabilir:

mov eax,eax  new h = eax, old h = eax // which does not mean anything 
movsxd r11,rbx convert with sign nhashprime stored in rbx into temp registry r11 
cwd    signed rax into rdx:rax 
idiv r11   signed divide rdx:rax by r11 -> rax=quotient, rdx=remainder 
mov rax,rdx  store remainder rdx into rax 

Eğer kod üretme optimizasyonu sağlayan deneyin mü? Sanırım, mov eax,eax'un içeriğini düzeltir.

Ama orijinal kodu da alt optimize edilmiştir. Durumunuzda imzasız aritmetik kullanmalısınız.

iyi edersiniz ikisinin nhashprime bir güce, yavaş bölünme basit and ikili işlem yerine bilgi işlem kullanmalısınız: Bu kod çok daha hızlı olacaktır

var h, nhashprimeMask: cardinal; // use UNSIGNED arithmetic here! 

// here nhashprime is a POWER OF TWO (128,256,512,1024,2048...) 
nhashprimeMask := nhashprime-1; // compute the mask 

while Assigned(p) do begin 
    np:= p^.next; 
    h:= leaf_hash(p^.data) and nhashprimeMask; 
    p^.next:= nhashtab[h]; 
    nhashtab[h]:= p; 
    p:= np; 
end; { while } 

, ve çok daha iyi derlemek gerekir.

+2

Hashtab için asal bir boyuttan başka bir şey daha fazla karma çarpışmalara yol açacaktır. Şu anda tam olarak teorik optimum seviyedeyim. Eğer ilk sayıyı iki güç ile değiştirirsem, karma çarpışmalar yukarı doğru gidiyor (% 18'lik çarpışmalar haltestte% 81'e düşüyor) – Johan

+0

Evet, optimizasyon – Johan

+0

'da bulunuyor. Verileri kardinaller için düzeltirken sorun ortadan kalkıyor. Optimizasyonu ** kapatıp ** kapattığınızda sorun da ortadan kalkar. – Johan