我最新的娛樂項目是用C++編寫一個brainfuck解釋器。這很簡單,但今天我決定通過彙編步驟添加它。最終的目標是能夠創建可執行文件,但現在它所做的只是一些基本的優化。例如+++++被轉換爲5添加1命令到單個添加5等等。爲什麼std :: map讓我的代碼變得如此臃腫?
雖然這個效果很好,但我注意到我的可執行文件被剝離後的大小從9k變爲12k。經過一番研究,我已經確定下面的功能是責怪,特別是地圖。我不懂爲什麼。
void Brainfuck::compile(const string& input) {
map<char, pair<Opcode, int>> instructions {
{ '<', make_pair(Opcode::MOVL, 1) },
{ '>', make_pair(Opcode::MOVR, 1) },
{ '+', make_pair(Opcode::INCR, 1) },
{ '-', make_pair(Opcode::DECR, 1) },
{ '[', make_pair(Opcode::WHILE, 0) },
{ ']', make_pair(Opcode::WEND, 0) },
{ ',', make_pair(Opcode::INP, 0) },
{ '.', make_pair(Opcode::OUTP, 0) },
};
string::const_iterator c = begin(input);
while (c != end(input)) {
if (instructions.find(*c) != end(instructions)) {
auto instruction = instructions[*c];
makeOp(c, instruction.first, instruction.second);
} else {
c++;
}
}
}
地圖中的關鍵是8個有效的Brainfuck操作之一。該函數遍歷輸入字符串並查找這些有效字符。按照Brainfuck規範跳過一個無效字符。如果它找到一個,它會將該鍵的映射值傳遞給一個名爲makeop的函數進行優化,將其轉換爲op結構並將其添加到解釋器實際執行的指令向量中。
op結構體有兩個成員。一個操作碼,它是基於uint8_t的作用域枚舉,表示8個操作之一,以及一個包含操作數的int。 (現在是一個操作數,未來更復雜的版本可能有多個操作數的指令)因此,在+++++示例中,結構將包含Opcode :: INCR和5.因此,條目是一個由操作碼和操作數組成的std :: pair。我意識到一些通用數據結構的開銷是不可避免的,但考慮到上面描述的簡單性,難道你不認爲3k有點過分嗎?
或者這可能不是有效實現我的目標的正確方法嗎?但是,如果不是std :: map,那麼應該使用哪種數據結構?
更新:
感謝所有誰回答。首先,談談我的動機。我經常在日常工作中不使用C++。所以我正在做一些娛樂項目來保持我的知識清晰。具有絕對最小的代碼尺寸並不像例如清晰,但我很有興趣瞭解如何發生膨脹和這樣的事情。
下面給出的建議,我的函數現在看起來是這樣的:
static const int MAXOPS = 8;
void Brainfuck::compile(const string& input) {
array<tuple<char, Opcode, int>, MAXOPS> instructions {
make_tuple('<', Opcode::MOVL, 1),
make_tuple('>', Opcode::MOVR, 1),
make_tuple('+', Opcode::INCR, 1),
make_tuple('-', Opcode::DECR, 1),
make_tuple('[', Opcode::WHILE, 0),
make_tuple(']', Opcode::WEND, 0),
make_tuple(',', Opcode::INP, 0),
make_tuple('.', Opcode::OUTP, 0),
};
string::const_iterator c = begin(input);
while (c != end(input)) {
auto instruction = find_if(begin(instructions), end(instructions),
[&instructions, &c](auto i) {
return *c == get<0>(i);
});
if (instruction != end(instructions)) {
makeOp(c, get<1>(*instruction), get<2>(*instruction));
} else {
c++;
}
}
}
我重新編譯和......什麼都沒有發生。我記得我有另一種方法,其中包含一張地圖和一個(現在已刪除?)的迴應暗示,僅僅有一個地圖實例就足以添加代碼。所以我將該映射更改爲數組並重新編譯。這次我剝離的大小爲 ,我的可執行文件從12280變爲9152.
地圖分別爲每個條目使用動態分配。如果代碼大小是目標,那麼也許正常的查找表會更好。 –
3k擁有構建,搜索和銷燬紅黑樹所需的所有代碼。對我來說聽起來不太古怪。 –
Fwiw,3kb實際上什麼都不是,除非你在某些嵌入式平臺上進行這樣的操作,這些嵌入式平臺有大量的存儲/內存限制。 –