2012-03-07 30 views
-1

私はC/C++を使用して高速JITを構築しようとしています。私のバイトコードをIA32に変換する必要があります。はい、私はlibjitなどについて知っていますが、私は彼らがこれより単純ではないと確信しています。私は命令を作成するためのより速い方法を見つけたと思ったが、私は間違っていた - 従来のスイッチ/ケースの方法は、私より2倍速かった。私のやり方は、ブロック全体をコピーして、テンプレートを塗りつぶすことです。私は現代コンパイラのスイッチ/ケースがジャンプテーブルを作成することを知っているので、私はジャンプテーブルを実装していませんでした。単純なバイトコードトランスレータ

ベンチマークにループされたcode []の内容を含む50MBのファイルを使用しました。 私の設定は:VS2010を使用してコンパイルされたi7、8GB RAMです。

#include <stdio.h> 
#include <stdlib.h> 
#include <memory.h> 
#include <time.h> 

//unsigned char code[] = {1,2,3,4,4}; 

void mycopy(unsigned char* to, unsigned char* from, int size) { 
    int i = 0; 
    while(i < size) { 
     /*if (i < size-3) { 
      if (*(unsigned int*) &from[i] == 0xFFFFFFFF) { 
       *(unsigned int*) &to[i] = 0xC3C3C3C3; 
       i += 4; 
       continue; 
      } 
     }*/ 

     to[i] = from[i]; 
     i++; 
    } 
} 

void translateA(unsigned char* code, unsigned char* output, int size) { 

    unsigned char A[] = { 3, 1, 1, 1 }; // { size, <bytes...> } 
    unsigned char B[] = { 2, 2, 2 }; 
    unsigned char C[] = { 8, 3, 3, 3, 3, 0xFF, 0xFF, 0xFF, 0xFF }; 
    unsigned char D[] = { 1, 4 }; 

    void* templat[] = { &A, &B, &C, &D }; 

    int i = 0; 
    int total = 0; 
    while(i < size) { 
     int op_index = (int) code[i] - 1; 
     unsigned char* instr_buffer = (unsigned char*) templat[op_index]; 
     int size = (int) instr_buffer[0]; 
     instr_buffer++; 
     mycopy(output+total, instr_buffer,size); 
     total += size; 
     i++; 
    } 
} 




void translateB(unsigned char* code, unsigned char* output, int size) { 
    for(int i = 0; i < size; i++) { 
     switch(code[i]) { 
      case 1: 
       output[0] = 1; 
       output[1] = 1; 
       output[2] = 1; 
       output += 3; 
       break; 
      case 2: 
       output[0] = 2; 
       output[1] = 2; 
       output += 2; 
       break; 
      case 3: 
       output[0] = 3; 
       output[1] = 3; 
       output[2] = 3; 
       output[3] = 3; 
       output[4] = 0xC3; 
       output[5] = 0xC3; 
       output[6] = 0xC3; 
       output[7] = 0xC3;    
       output += 8;    
       break; 
      case 4: 
       output[0] = 4; 
       output++; 
       break; 
     } 
    } 
} 

int main(int argc, char* argv[]) { 
    // load the 'code' to an array 
    FILE* f = fopen("testops.bin", "r+"); 

    fseek(f, 0, SEEK_END); 
    long fsize = ftell(f); 
    fseek(f, 0, SEEK_SET); 

    unsigned char* code = (unsigned char*) malloc(fsize); 
    fread(code, fsize, 1, f); 
    fclose(f); 

    unsigned char* output = (unsigned char*) malloc(fsize*10); 
    memset(output, 0x7A, fsize*10); 

    // benchmark it 
    time_t start = clock(); 

    // Replace with translateB. It's ~2x faster 
    translateA(code, output, fsize); 

    printf("\nelapsed %fs\n\n", (float) (clock()-start)/1000); 

    printf("OUTPUT: "); 
    for(int i=0;i<1024;i++) { 
     if (output[i] == 0x7A) break; 
     printf("%X", output[i]); 
    } 

    printf("\n"); 

    system("PAUSE"); 
    return 0; 
} 

EDIT:

は、ここに私のコードです私の質問は、なぜスイッチコードより速いですか?私が間違っていることはありますか?

+3

あなたの質問は何ですか? – orlp

+0

私の質問はなぜスイッチコードが高速ですか? – blez

+0

私は自分の質問を編集しました。 – blez

答えて

0

プログラムをビルドしてから逆アセンブルすると、応答が得られます。

スイッチケースは、すでに述べたようにジャンプテーブルを作成するため、コンパイラの最適化により効率的です。

このメソッドでは、より多くのアドレス参照が行われ、1つの関数呼び出しと1つのバイトコードごとにループが追加されます。

さらに、繰り返しの数字を知っているループにはなぜwhileを使用しますか? コンパイラでループの追加の最適化が行われている可能性があります(for)。

スイッチを4つ使用すると、コンパイラがジャンプテーブルを作成するかどうかわからないため、実際の例がさらに高速になる可能性があります。