2010-04-17 132 views
7

我已經在C++中爲我創建的語言構建了一個解釋器。C++解釋器概念問題

設計中的一個主要問題是我在語言中有兩種不同的類型:數字和字符串。所以,我必須繞過像一個結構:

class myInterpreterValue 
{ 
myInterpreterType type; 
int intValue; 
string strValue; 
} 

此類的對象是在例如爲:在我的語言倒計時循環大約爲300萬次,第二次通過。

性能分析指出:85%的性能被字符串模板的分配函數所佔用。

這對我來說很清楚:我的解釋器設計不好,不足以使用指針。然而,我沒有選擇:在大多數情況下我不能使用指針,因爲我只需要複製。

如何做到這一點?像這樣的課是一個更好的主意嗎?

vector<string> strTable; 
vector<int> intTable; 
class myInterpreterValue 
{ 
myInterpreterType type; 
int locationInTable; 
} 

所以類只知道什麼類型它代表,並在表

然而,這一次有缺點的位置: 我不得不臨時值添加到字符串/ INT矢量表,然後再次移除它們,這將再次吃掉很多表演。

  • 幫助,Python或Ruby等語言的解釋器如何做到這一點?它們以某種方式需要一個表示語言中的值的結構,如可以是int或string的某個值。
+0

你的字符串是否可變? – 2010-04-18 00:07:21

+0

他們和這個類一般用於一切:標識符,值等。所以是的。 – 2010-04-18 00:14:10

回答

3

我懷疑許多值不是字符串。所以你可以做的第一件事是擺脫string對象,如果你不需要它。把它放進一個工會。另一件事是,可能很多字符串都很小,因此如果在對象本身中保存小字符串,就可以擺脫堆分配。 LLVM爲此提供了SmallString模板。然後你可以使用字符串實習,也可以這樣回答。 LLVM的StringPool類是這樣的:調用intern("foo")並獲得一個智能指針,指向其他myInterpreterValue對象可能使用的共享字符串。

工會可以這樣寫

class myInterpreterValue { 
boost::variant<int, string> value; 
}; 

boost::variant爲你做的類型標記。如果你沒有提升,你可以像這樣實現它。在C++中不能輕鬆地獲得對齊,所以我們將一些可能需要大量對齊的類型推入存儲聯合。

class myInterpreterValue { 
union Storage { 
    // for getting alignment 
    long double ld_; 
    long long ll_; 

    // for getting size 
    int i1; 
    char s1[sizeof(string)]; 

    // for access 
    char c; 
}; 
enum type { IntValue, StringValue } m_type; 

Storage m_store; 
int *getIntP() { return reinterpret_cast<int*>(&m_store.c); } 
string *getStringP() { return reinterpret_cast<string*>(&m_store.c); } 


public: 
    myInterpreterValue(string const& str) { 
    m_type = StringValue; 
    new (static_cast<void*>(&m_store.c)) string(str); 
    } 

    myInterpreterValue(int i) { 
    m_type = IntValue; 
    new (static_cast<void*>(&m_store.c)) int(i); 
    } 
    ~myInterpreterValue() { 
    if(m_type == StringValue) { 
     getStringP()->~string(); // call destructor 
    } 
    } 
    string &asString() { return *getStringP(); } 
    int &asInt() { return *getIntP(); } 
}; 

你明白了。

+0

應提供一種方法來檢查存儲的值的類型,並在類型錯誤時在asX中引發異常。 (@OP:我想後者在這個例子中被清除了,但是你需要這樣做。) – 2010-04-18 00:56:28

+0

好吧,最好的方法是使用'Boost.Variant',無論如何,litb只是在演示:) – 2010-04-18 10:34:50

+0

不會建議這樣做,因爲它使用了不安全的技巧,而完全有可能避開它們。另一個問題是它使調試更加困難。 – Smilediver 2010-04-18 10:41:12

1

我認爲一些動態語言會在運行時使用散列查找緩存所有等效字符串,並且只存儲指針。在字符串保持不變的循環的每次迭代中,因此,只會有一個指針分配或至多一個字符串散列函數。我知道一些語言(Smalltalk,我想?)不僅用字符串而且用小數字來做這件事。見Flyweight Pattern

IANAE在這一個。如果這沒有幫助,你應該給循環代碼,並引導我們瞭解它的解釋。

0

解決這個問題的最簡單方法是使它成爲指向字符串的指針,並且只在創建字符串值時分配它。您也可以使用聯合來保存內存。

class myInterpreterValue 
{ 
myInterpreterType type; 
union { 
    int asInt; 
    string* asString; 
} value; 
} 
1

在Python和Ruby中,整數都是對象。所以這不是一個「價值」是一個整數還是一個字符串的問題,它可以是任何東西。而且,這兩種語言的所有內容都是垃圾收集。不需要複製對象,指針可以在內部使用,只要它們安全地存儲在垃圾收集器可以看到的地方。

所以,一個解決問題的方法是:

class myInterpreterValue { 
    virtual ~myInterpreterValue() {} 
    // example of a possible member function 
    virtual string toString() const = 0; 
}; 

class myInterpreterStringValue : public myInterpreterValue { 
    string value; 
    virtual string toString() const { return value; } 
}; 

class myInterpreterIntValue : public myInterpreterValue { 
    int value; 
    virtual string toString() const { 
     char buf[12]; // yeah, int might be more than 32 bits. Whatever. 
     sprintf(buf, "%d", value); 
     return buf; 
    } 
}; 

然後使用虛擬電話和dynamic_cast接通或檢查的類型,而不是針對myInterpreterType的值進行比較。

在這一點上通常要做的事情是擔心虛擬函數調用和動態轉換可能會很慢。 Ruby和Python都使用虛擬函數調用。雖然不是C++虛擬調用:對於這兩種語言,他們的「標準」實現都是用C語言定製的多態性機制。但原則上沒有理由認爲「虛擬」意味着「表現出窗外」。這就是說,我認爲他們可能對整數的某些用法有一些明智的優化,包括作爲循環計數器。但是,如果您目前看到您的大部分時間都花在複製空字符串上,那麼通過比較的虛擬函數調用幾乎是即時的。

真正的擔心是你將如何進行資源管理 - 取決於你的解釋語言的計劃,垃圾收集可能比你想要的更麻煩。