简体   繁体   English

汇编器快速排序比 java array.sort 慢?

[英]Assembler quicksort slower than java array.sort?

I have coded an assembler QuickSort and found that is it slower than the array.sort in java.我编写了一个汇编程序 QuickSort,发现它比 java 中的 array.sort 慢。 I am wondering if anyone would know of why I cannot match the speed of some of the higher level languages (I am assuming C would probably be quicker also)我想知道是否有人会知道为什么我无法匹配某些高级语言的速度(我假设 C 可能也会更快)

I have tested with 1,000,000 * 1024 bytes.我已经测试了 1,000,000 * 1024 字节。 I get approx 2 seconds to complete the sort while Array.Sort in Java will do it in about.8 second我大约需要 2 秒来完成排序,而 Java 中的 Array.Sort 将在大约 8 秒内完成

I don't think there is any problem in actually moving the data as I have independently tested that and it moves quite quickly.我认为实际移动数据没有任何问题,因为我已经独立测试过它并且移动得非常快。

    %macro $copyBytes 3
        mov RSI,%1                                  ; Grab the Src Address
        mov RDI,%2                                  ; Grab the Dst Address
        mov rcx,%3                                  ; Grab the length
    %%l:movsb                                       ; Move the data
        loop %%l    
    %endmacro

    %imacro $getAddress 2
        mov %1,r_Table                              ; Start of Table to RSI/RDI
        mov rax,%2                                  ; Lower/Upper to RAX
        mul r_recordLength                          ; Multiply by Record Length
        add %1,rax                                  ; add offset to RSI/RDI
    %endmacro

    %define r_recordLength  r8
    %define r_startPos      r10             
    %define r_noOfBytes     r11
    %define r_Ubound        r15

    %define @left           r10
    %define @right          r11
    %define @pivotValue     r12
    %define @prevLeft       r13

    [section .bss]
        d_startPos          resq 1
        d_noOfBytes         resq 1      
        pivotValue          resb 128                ; This gives tha max sortkey size
        LeftAddress         resq 1
        RightAddress        resq 1
    [section .text] 
;   Call the recursive SORT
    push 0                                          ; Push the 1st Left
    push r_Ubound                                   ; Push the 1st Right
    Call _QSort
    pop rax
    pop rax


;-----------------------------------------------------------------------
;   Recursive Sort
;-----------------------------------------------------------------------
_QSort:

    mov @left,qword[rsp+16]                         ; get the Left Index
    mov @right,qword[rsp+8]                         ; get the Right Index

;   if  left >= right                   ; Exit when Left is not < Right
    cmp @left,@right
        jnl .Exit

;   pivotValue = RandomNo[ [ left + right / 2 ] ] CALCULATE pivotValue
    mov rax,@left                                   ; Grab the left
    add rax,@right                                  ; add the right
    shr rax,1                                       ; and divide by 2
    $getAddress RSI,rax                          ; get the address of this slot
    $copyBytes RSI,pivotValue,qword[d_noOfBytes]     ; and copy it to pivotValue


;   prevLeftIdx = [moveThem]
    push @left                                      ; preserve the Left index
    push @right                                     ; preserve the Right index
    call _moveThem                                  ; do the moves
    pop @right                                      ; restore the Right index
    pop @left                                       ; restore the Left index                                                        
    mov @prevLeft,rax                               ; and save the returned value

;   -------------------------------------
;   Recursive Call - (left, prevLeft - 1)
;   -------------------------------------
    push @left                                      ; preserve the Left index
    push @right                                     ; preserve the Right index
    push @left                                      ; push the 1st parameter (Left)
    mov rax,@prevLeft                               ; get the previous left
    dec rax                                         ; minus 1
    push rax                                        ; push the 2nd parameter
    Call _QSort                                     ; Call yourself     
    pop rax                                         ; Junk this
    pop rax                                         ; Junk this
    pop @right                                      ; restore the Right index
    pop @left                                       ; restore the Left index

;   ----------------------------------
;   Recursive Call - (prevLeft, right)  
;   ----------------------------------
    push @left                                      ; preserve the Left index
    push @right                                     ; preserve the Right index
    push @prevLeft                                  ; push the 1st parameter (previous left
    push @right                                     ; push the 2nd parameter (Right0
    Call _QSort                                     ; Call yourself
    pop rax                                         ; Junk this
    pop rax                                         ; Junk this
    pop @right                                      ; restore the Right index
    pop @left                                       ; restore the Left index

    .Exit:
_QSort_EXIT:
    ret

_moveThem:

;   repeat.while (left <= right)
    cmp @left,@right                                ; Compare Left to Right Index
        jg .Exit                                    ; exit if Left is > Right

;   repeat.while (Key[left] < pivotValue)
;           left    = left + 1
;   end.repeat
    .start1:$getAddress RSI,@left                    ; Get the Start of the record 
            add RSI,qword[d_startPos]               ; add the requested starting position
            mov qword[LeftAddress],RSI              ; and save it
            mov RDI,pivotValue                      ; Get the start of the pivotValue
            mov rcx,qword[d_noOfBytes]              ; Grab the length (LOOP Ctr)
            cld                                     ; Clear the direction flag - left to right
    repe    cmpsb                                   ; Compare the String
            jnl .cont1                              ; If result not < than exit While Loop                                          
            inc @left                               ; get the next table slot
            jmp .start1                             ; and repeat
    .cont1:

    cmp @left,@right                                ; Compare Left to Right Index
        jg .Exit                                    ; exit if Left is > Right

;   repeat.while (Key[right]> pivotValue)
;       right   = right - 1
;   end.repeat
    .start2:$getAddress RSI,@right                   ; Get the Start of the record 
            add RSI,qword[d_startPos]               ; add the requested starting position
            mov qword[RightAddress],RSI             ; and save it
            mov RDI,pivotValue                      ; Get the start of the pivotValue 
            mov rcx,qword[d_noOfBytes]              ; Grab the length (LOOP Ctr)
            cld                                     ; Clear the direction flag - left to right
    repe    cmpsb                                   ; Compare the string    
            jng .cont2                              ; If result not > than exit While Loop  
            dec @right                              ; get the previous table slot
            jmp .start2                             ; and repeat
    .cont2:

    cmp @left,@right                                ; Compare Left to Right Index
        jg .Exit                                    ; exit if Left is > Right
        jl .DoTheMove                               ; Do the move if less than

    ;   No need to do the move if left = right
        inc @left                                   ; increment the left 
        jmp .Exit                                   ; and head for the exit


;   if left <= right - SWAP left and right data
.DoTheMove:
    mov RSI,qword[LeftAddress]                      ; grab the Left address
    mov RDI,qword[RightAddress]                     ; grab the Right address        
    mov rcx,r_recordLength                          ; and grab the record length
    .start3:cmp rcx,8                               ; Have we still got 8 bytes to move
                jb .start4                          ; If not go and pick up the last few bytes
            mov rax,qword[RSI]                      ; grab the left 8 bytes                                     
            mov rbx,qword[RDI]                      ; grab the right 8 bytes
            xchg rax,rbx                            ; swap them over
            mov qword[RSI],rax                      ; put the right 8 bytes to the left
            mov qword[RDI],rbx                      ; put the left 8 bytes to the right 
            add RSI,8                               ; setup RSI for the next 8 bytes
            add RDI,8                               ; setup RDI for the next 8 bytes                                                    
            sub rcx,8                               ; take 8 away from the counter
            jmp .start3                             ; and loop around

    .start4:cmp rcx,0                               ; Have we still got anything to move
                jna .cont4                          ; If not we are done            
            mov ah,byte[RSI]                        ; grab the left byte                                            
            mov al,byte[RDI]                        ; grab the right byte
            xchg ah,al                              ; swap them over
            mov byte[RSI],ah                        ; put the right byte to the left
            mov byte[RDI],al                        ; put the left byte to the right
            add RSI,1                               ; setup RSI for the next byte
            add RDI,1                               ; setup RDI for the next bytes
            sub rcx,1                               ; take 1 away from the counter
            jmp .start4                             ; and loop around
    .cont4:

    inc @left                                       ; Next Left
    dec @right                                      ; Previous Right
    jmp _moveThem                                   ; Back to the Start

.Exit:  
    mov rax,@left                                   ; Return the Left value

_moveThem_EXIT:
    ret
import java.util.Arrays;

class QuickSort {

    public static void Sort(String[] strArray, int lowerIndex, int higherIndex) 
    {        
        int left = lowerIndex;
        int right= higherIndex;
        int x;
        String pivot, temp;

        pivot = strArray[lowerIndex+(higherIndex-lowerIndex)/2];

        while (left <= right) {

          x = strArray[left].compareTo(pivot);
          while (x < 0) {
            left++;  
            x = strArray[left].compareTo(pivot);
          }

          x = strArray[right].compareTo(pivot);
          while (x > 0) {
            right--;
            x = strArray[right].compareTo(pivot);
          }

          if (left <= right) {
            temp = strArray[left];
            strArray[left] = strArray[right];
            strArray[left] = temp;
            left++;
            right--;
          }
        }

        if (lowerIndex < right)
            Sort(strArray,lowerIndex,right);

        if (left < higherIndex)
            Sort(strArray,left,higherIndex);
    }
}

public class V2_11 {

  public static void main(String[] args) {

    String strArray[] = new String[1000001];       
    String letters[] = {"a","b","c","d","e","f","g","h","i","j",
                        "k","l","m","n","o","p","q","r","s","t",
                        "u","v","w","x","y","z"};
    String word;
    String spaces[] = new String[998];
    int random;

  //---------------
  //Build the Array
  //---------------
    System.out.println("Filling Array");  
    System.out.println(java.time.LocalTime.now());  
    for (int i=0;i < 1000000;i++){ 
      word="";
      for (int j=0;j< 26;j++){ 
        random = (int)(Math.random() * 9 + 0);
        word=word+letters[random];
      }
      strArray[i]=word+spaces;
    }   
    System.out.println(java.time.LocalTime.now());

  //--------------
  //Sort the Array
  //--------------
    System.out.println("Sorting");  
    System.out.println(java.time.LocalTime.now());    
  //---Arrays.sort(strArray);
    QuickSort.Sort(strArray,0, 999999);   
    System.out.println(java.time.LocalTime.now());

  //-------------------
  //Display some Reults
  //-------------------
    for (int i=100;i< 110;i++)
    { 
      System.out.println(strArray[i]);
    }
  }
}
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>

    struct timeval start, stop;
    double secs = 0;

    char strArray[1000000][27];
    char pivotValue[27]; 
    char temp[27]; 
    int prevLeftIdx;

int moveThem(int left, int right)
{   int x;

    while (left <= right) {

       x = strcmp(strArray[left],pivotValue);
       while (x < 0) {
         left++;  
            x = strcmp(strArray[left],pivotValue);
       }

       x = strcmp(strArray[right],pivotValue);
       while (x > 0) {
         right--;
         x = strcmp(strArray[right],pivotValue);
       }

       if (left <= right) {
         strcpy(temp,strArray[left]);
         strcpy(strArray[left],strArray[right]);
         strcpy(strArray[right],temp);
         left++;
         right--;
       }
    }

    return left;

}

int QuickSort(int left, int right) 
{

    if (left>=right)
        {return 0;}

    strcpy(pivotValue, strArray[(left+right)/2]);
    prevLeftIdx=moveThem(left,right);

    QuickSort(left,prevLeftIdx-1);
    QuickSort(prevLeftIdx,right);      

}


int main()
{

    char letters[26] = "abcdefghijklmnopqrstuvwxyz";
    char c_Null[1] = "\0";
    char word[27];
    int i,j,random;

  //---------------
  //Build the Array
  //---------------
    puts("Filling Array"); 

    gettimeofday(&start, NULL);
    srand(time(NULL));
    for (i=0;i < 1000000;i++){ 
      for (j=0;j< 26;j++){ 
        random = rand() % 24;
        word[j] = letters[random];
      }
      word[j] = *c_Null;
      strcpy(strArray[i],word);
    }   
    gettimeofday(&stop, NULL);
    secs = (double)(stop.tv_usec - start.tv_usec) / 1000000 + (double)(stop.tv_sec - start.tv_sec);
    printf("time taken %f\n",secs);

  //--------------
  //Sort the Array
  //--------------
    puts("Sorting"); 

    gettimeofday(&start, NULL);   
    QuickSort(0, 999999);   
    gettimeofday(&stop, NULL);
    secs = (double)(stop.tv_usec - start.tv_usec) / 1000000 + (double)(stop.tv_sec - start.tv_sec);
    printf("time taken %f\n",secs);


  //-------------------
  //Display some Reults
  //-------------------
   for (i=0;i<10 ;i++)
   { 
     puts(strArray[i]); 
   }

}
RESULTS FOR C program
Filling Array
time taken 0.245878
Sorting
time taken 0.473481
aaaagajqpbutmaxxhgjkauwmwg
aaaakestelpvsrgiemafwtujev
aaaamnvrdueegvfilkvsabdomd
aaaapspaurwurvecikmjibkwfk
aaaauclobckwflwushhnhiafws
aaabaiueabmqlumijuhlrvmgtf
aaabcqehhoeegkhpeondnbsqjb
aaabmwxdrcpfxlhbmggxvjcaoc
aaabplwowigaxmpjipqxkntilc
aaacjsuqkmbwnglltqsspvvtlr

Found a solution to this that at least satisfies me.找到了一个至少让我满意的解决方案。

As a starting point the quicksort algorithm performs as follows on a 1,000,000 element array of qwords.作为起点,快速排序算法对 1,000,000 个元素的 qwords 数组执行如下操作。 (Times shown in seconds for 5 runs).091067, .087505, .087407, .088104, .086983 - So..pretty fast (以秒为单位显示 5 次运行的时间).091067、.087505、.087407、.088104、.086983 - 非常快

On a 1,000,000 * 1024byte string array sorting on the 1st 8 bytes times were:- 2.265867, 2.241919, 2.281978, 2.264712, 2.291069 - so..not so hot在 1,000,000 * 1024byte 字符串数组中,第 1 个 8 字节时间排序为:- 2.265867、2.241919、2.281978、2.264712、2.291069 - 所以..不太热

On a 1,000,000 * 1024byte string array sorting on the 1st 8 bytes (After code change) times were:- 0.719363, 0.688310, 0.717557, 0.710861, 0.746405 - comparible if not better than Java在 1,000,000 * 1024byte 字符串数组上排序的第一个 8 个字节(代码更改后)时间为:- 0.719363、0.688310、0.717557、0.710861、0.746405 - 如果不优于 ZD52387880E1EA22817A72D37592138,则可比较

The code fix consisted of replacing代码修复包括替换

     mov rsi,Src Address
     mov rdi,Dst Address
     mov rcx,SortKey Length
repe cmpsb

with

    mov rax,[Src Address]
    bswap rax
    mov rbx,[Dst Address]
    bswap
    cmp rax,rbx

I guess you are making the compares 8bytes at once and it make sense that it would be quicker.我猜您一次比较 8 个字节,这样会更快是有道理的。 This is obviously NOT the best way to do it but without getting side tracked into something I didn't want to get into, it will do well enough.这显然不是最好的方法,但如果不被卷入我不想进入的事情,它会做得很好。

As a side note, the code needs completing to deal with variable length keys but that is just a case of sitting down and doing the work.作为旁注,代码需要完成以处理可变长度的键,但这只是坐下来完成工作的情况。 I hope this has helped anyone who has stumbled across this thread.我希望这对任何偶然发现此线程的人有所帮助。 (Special thx to @PeterCordes for his efforts) (特别感谢@PeterCordes 的努力)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM