fmt vs sprintf vs stringstream 速度比較
目標
粗略的比較這些字串拼接函數
fmt::format
: 並不是c++20
的 std::format ,是在 ubuntu20.04 的 apt repo 裡面可以直接安裝的 libfmt-dev: 6.1.2std::sprintf
: c 本來就有的函數std::stringstream
: c++ 的另一種字串拼接方式
由於我是在嘗試一個把混有字串和數字的 tuple 轉換成 string 的方式, example:
struct AreaData {
std::string stateName;
int typeID;
};
我希望能夠有「toString(a1) == toString(a2)
=> a1 == a2
」,所以我會用這種方式把 string 拼進去:
{str.length}\t{str.data}\t
這樣就做到那件事。
而測試時也只會測試把一個字串和一個數字披在一起:
{stateName.size}\t{stateName.data}\t{typeID}\t
Code
fmt::format
這不需要什麼特別注意的地方,不過為了安全,我在拼接 string 時是用指定 (data, size) 的方式:{:.{}}
fmt::format("{}\t{:.{}}\t{}\t", strv1.length(), strv1.data(), strv1.length(), intv);
Sprintf
本來的 sprintf 並不安全,因為他是吃一個指標,並且沒有長度的檢查。所以要用 snprintf
template<typename ... Args>
std::string string_format( const std::string& format, Args ... args )
{
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
auto size = static_cast<size_t>( size_s );
std::unique_ptr<char[]> buf( new char[ size ] );
std::snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
然後根據 Using printf with a non-null terminated string 做出的 format string:
string_format("%d\t%.*s\t%d\t", strv.length(), strv.length(), strv.data(), intv);
std::stringstream
std::stringstream ss;
ss << strv1.length() << "\t";
ss.write(strv1.data(), strv1.length()) << "\t";
ss << intv << "\t";
auto val = ss.str();
結果與結論
在 ubuntu 20.04 環境,每個都跑 10^8 次數下的結果:
fmt::format
: 15 秒sprintf
: 39 秒std::stringstream
: 60 秒
節論: fmt::format
是個不錯的選擇,不過假如在不需要考慮速度,並且也不太希望增加依賴的情況其實也沒有很必要?