2016-10-27 40 views
0

我最新的娛樂項目是用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.

+0

地圖分別爲每個條目使用動態分配。如果代碼大小是目標,那麼也許正常的查找表會更好。 –

+1

3k擁有構建,搜索和銷燬紅黑樹所需的所有代碼。對我來說聽起來不太古怪。 –

+0

Fwiw,3kb實際上什麼都不是,除非你在某些嵌入式平臺上進行這樣的操作,這些嵌入式平臺有大量的存儲/內存限制。 –

回答

3

map內部使用平衡樹來存儲元素。平衡樹很快,但需要一些代碼開銷來重新平衡樹插入或刪除。所以這個代碼的3k合理地接​​縫。

+0

我現在明白了。謝謝。 – Jaldhar