2012-07-29 218 views
0

我正在創建腳本語言,首先解析代碼 ,然後將函數(執行代碼)複製到一個緩衝區\內存作爲解析的代碼。在運行時編譯函數C++編譯時編譯的函數的數量

有一種方法來複制功能的二進制代碼來緩衝,然後執行整個緩衝區? 我需要一次執行所有的功能以獲得更好的性能。

要明白我的問題,以最好的,我想要做這樣的事情:

#include <vector> 
using namespace std; 

class RuntimeFunction; //The buffer to my runtime function 

enum ByteCodeType { 
    Return, 
    None 
}; 

class ByteCode { 
    ByteCodeType type; 
} 

void ReturnRuntime() { 
    return; 
} 

RuntimeFunction GetExecutableData(vector<ByteCode> function) { 
    RuntimeFunction runtimeFunction=RuntimeFunction(sizeof(int)); //Returns int 
    for (int i = 0 ; i < function.size() ; i++) { 
     #define CurrentByteCode function[i] 
     if (CurrentByteCode.Type==Return) { 
      runtimeFunction.Append(&ReturnRuntime); 
     } //etc. 
     #undef 
    } 
    return runtimeFunction; 
} 

void* CallFunc(RuntimeFunction runtimeFunction,vector<void*> custom_parameters) { 
    for (int i=custom_parameters-1;i>=0;--i) { //Invert parameters loop 
     __asm { 
      push custom_parameters[i] 
     } 
    } 
    __asm { 
     call runtimeFunction.pHandle 
    } 
} 
+1

麥克·鮑爾(LuaJIT2的)評論(http://article.gmane.org/gmane.comp.lang.lua.general/75426)都非常值得一讀。 – ephemient 2012-07-29 01:29:57

+0

謝謝,但我不需要在運行時生成代碼。我需要結合一些函數(也使用const參數)。我只是不想見到大會,我討厭它。 – 2012-07-29 01:32:20

+0

你的目標(更好的表現,不學會集會)是相互矛盾的。實際上,爲了獲得良好的性能,您需要比彙編更低的級別,並瞭解高速緩存行爲,流水線,數據依賴性等。 – 2012-07-29 03:41:42

回答

3

有許多這樣做,這取決於你想有多深,以進入發電運行時代碼的方法,但一個相對簡單的方法是使用線程代碼和線程代碼解釋器。

基本上,螺紋代碼由函數指針陣列的,且解釋器穿過陣列調用每個指向功能。棘手的部分是你通常每個函數都返回數組元素的地址,它包含一個指向下一個要調用的函數的指針,這允許你在解釋器中毫不費力地實現分支和調用等事情。

通常情況下,如:

typedef void *(*tc_func_t)(void *, runtime_state_t *); 

void *interp(tc_func_t **entry, runtime_state_t *state) { 
    tc_func_t *pc = *entry; 
    while (pc) pc = (*pc)(pc+1, state); 
    return entry+1; 
} 

這就是整個解釋器。 runtime_state_t是某種包含一些運行時狀態(通常是一個或多個堆棧)的數據結構。通過創建的tc_func_t函數指針陣列並與函數指針(以及可能的數據)填充它們,用一個空指針結束調用它,然後調用interp與含有數組的開始的變量的地址。所以,你可能有類似:

void *add(tc_func_t *pc, runtime_state_t *state) { 
    int v1 = state->data.pop(); 
    int v2 = state->data.pop(); 
    state->data.push(v1 + v2); 
    return pc; } 
void *push_int(tc_func_t *pc, runtime_state_t *state) { 
    state->data.push((int)*pc); 
    return pc+1; } 
void *print(tc_func_t *pc, runtime_state_t *state) { 
    cout << state->data.pop(); 
    return pc; } 

tc_func_t program[] = { 
    (tc_func_t)push_int, 
    (tc_func_t)2, 
    (tc_func_t)push_int, 
    (tc_func_t)2, 
    (tc_func_t)add, 
    (tc_func_t)print, 
    0 
}; 

void run_prgram() { 
    runtime_state_t state; 
    tc_func_t *entry = program; 
    interp(&entry, &state); 
} 

調用run_program運行的小程序,增加了2 + 2和輸出結果。

現在,您可能會對interp的稍微奇怪的調用設置感到困惑,entry參數中有一個額外的間接級別。這使您可以使用interp自己作爲一個線程代碼數組的函數,隨後指向另一個數組,它會做一個線程代碼調用。

編輯

像這樣的線程代碼與性能的最大問題 - 螺紋編碼的解釋是非常不友好的分支預測,所以性能是相當多,每一個線程指令調用鎖定分支錯誤預測恢復時間。

如果你想要更多的性能,你很可能要到全運行時代碼生成。 LLVM提供了一個很好的,與機器無關的接口,以及用於常見平臺的相當不錯的優化器,可以在運行時生成相當不錯的代碼。在[實施口譯]

+0

謝謝克里斯,但它是(性能相似)我目前的執行方式。當我解析代碼時,它給了地址和一個對象,然後推動對象並調用地址。順便說一句,你在這裏給了很酷的主意。 – 2012-07-29 08:39:35