簡體   English   中英

優化字符串創建

[英]Optimizing string creation

我有以下使用屬性設置文件名的類的模擬代碼:

#include <iostream>
#include <iomanip>
#include <sstream>

class Test {
    public:
        Test() { id_ = 1; }
        /* Code which modifies ID */

        void save() {
            std::string filename ("file_");
            filename += getID();
            std::cout << "Saving into: " << filename <<'\n';
        }

    private:
         const std::string getID() {
             std::ostringstream oss;
             oss << std::setw(4) << std::setfill('0') << id_;
             return oss.str();
         }

         int id_;
};

int main () {
    Test t;
    t.save();
}

我關心的是getID方法。 乍一看,這似乎效率很低,因為我正在創建ostringstream及其對應的string以返回。 我的問題:

1)由於返回const std::string ,編譯器(在我的情況下為GCC)是否可以對其進行優化?

2)有什么方法可以提高代碼的性能? 也許移動語義之類的東西?

謝謝!

1)由於返回const std :: string,編譯器(在我的情況下為GCC)是否可以對其進行優化?

除非通過引用返回,否則返回const對象沒有任何意義

2)有什么方法可以提高代碼的性能? 也許移動語義之類的東西?

id id_不會改變,只需在構造函數中創建值,使用靜態方法可能會有所幫助:

     static std::string format_id(int id) {
         std::ostringstream oss;
         oss << std::setw(4) << std::setfill('0') << id;
         return oss.str();
     }

接着:

Test::Test()
 : id_(1)
 , id_str_(format_id(id_))
{ }

更新:

由於id_確實發生了更改,因此該答案對於該問題並不完全有效,我不會刪除它,因為也許有人會發現它對他的情況有用。 無論如何,我想澄清一些想法:

  • 必須是靜態的才能在變量初始化中使用
  • 使用成員變量id_的代碼中有一個錯誤(現已更正)。
  • 按值返回const對象是沒有意義的,因為按值返回只會將結果復制(忽略優化)到新變量中,該新變量在調用方的范圍內(可能不是const)。

我的建議

一種選擇是更新id_str_隨時領域id_改變(你必須有一個setter id_ ),因為你已經起了變化成員id_我認為不會有任何問題,更新另一個。

這種方法允許將getID()實現為簡單的getter(應為const,btw),而不會出現性能問題,並且字符串字段僅計算一次。

在打開文件之類的昂貴操作之前,只需創建一次ostringstream ,就根本與程序的效率無關,所以不必擔心。

但是,您應該擔心代碼中表現出的一種壞習慣。 值得稱贊的是,您似乎已經確定了它:

1)由於返回const std::string ,編譯器(在我的情況下為GCC)是否可以對其進行優化?

2)有什么方法可以提高代碼的性能? 也許移動語義之類的東西?

是。 考慮:

class Test {
    // ...
    const std::string getID();
};

int main() {
    std::string x;
    Test t;
    x = t.getID();  // HERE
}

在標記為// HERE的行上,調用哪個賦值運算符? 我們想調用移動分配運算符,但是該運算符的原型為

string& operator=(string&&);

我們實際上是在傳遞給我們的參數operator=的類型是“引用類型的右值const string ” -即const string&& const正確性規則阻止我們將const string&&靜默轉換為string&& ,因此,當編譯器創建賦值運算符函數集時,可以在此處使用它( 重載set ),它必須排除move-assignment運算符需要string&&

因此, x = t.getID(); 最終調用了copy -assignment運算符(因為const string&&可以安全地轉換為const string& ),並且您制作了一個額外的副本,如果您沒有養成const的壞習慣的話,可以避免這種情況-限定返回類型。


同樣,當然, getID()成員函數可能應該聲明為const ,因為它不需要修改*this對象。

因此合適的原型是:

class Test {
    // ...
    std::string getID() const;
};

經驗法則是: 始終按值返回,而永不按const值返回。

您可以通過以下方式更改getID

std::string getID() {
  thread_local std::ostringstream oss;
  oss.str("");  // replaces the input data with the given string
  oss.clear();  // resets the error flags

  oss << std::setw(4) << std::setfill('0') << id_;
  return oss.str();
}

它不會每次都創建一個新的ostringstream

在您的情況下,這是不值得的(就像Chris Dodd所說的那樣, 打開文件並寫入文件可能會貴10到100倍 )...只是知道。

還應考慮在任何合理的庫實現中, std::to_string速度至少應與stringstream一樣快。

1)由於返回const std :: string,編譯器(在我的情況下為GCC)是否可以對其進行優化?

這種做法有一個基本原理,但實際上已過時(例如,Herb Sutter建議為非原始類型返回const值)。

強烈建議使用C ++ 11將值作為非常量返回,以便您可以充分利用右值引用。

關於此主題,您可以看一下:

一種可能性是做這樣的事情:

std::string getID(int id) { 
    std::string ret(4, '0') = std::to_string(id);

    return ret.substring(ret.length()-4);
}

如果您使用的是包含短字符串優化的實現(例如VC ++),則很有可能會顯着提高速度(使用VC ++進行的快速測試顯示,其速度大約是其4-5倍)。

OTOH,如果您使用的包含短字符串優化的實現,則很有可能會變慢 例如,使用g ++運行相同的測試,所產生的代碼將慢4-5倍。

還有一點: 如果您的ID號可能超過4位數字,這不會產生相同的行為-它總是返回恰好4個字符的字符串,而不是stringstream代碼創建的最少4個字符。 如果您的ID號碼可能超過9999,那么此代碼對您根本不起作用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM