Я написал следующую функцию конкатенации строк (join
), чтобы уменьшить количество распределений и время, затрачиваемое на построение окончательной строки. Я также хотел написать простую в использовании функцию добавления (если возможно, однострочную).
size_t str_size(const char *str) {
return std::strlen(str);
}
size_t str_size(const std::string &str) {
return str.size();
}
template <typename T>
size_t accumulated_size(const T& last) {
return str_size(last);
}
template <typename T, typename... Args>
size_t accumulated_size(const T& first, const Args& ...args) {
return str_size(first) + accumulated_size(args...);
}
template <typename T>
void append(std::string& final_string, const T &last) {
final_string += last;
}
template <typename T, typename... Args>
void append(std::string& final_string, const T& first, const Args& ...args) {
final_string += first;
append(final_string, args...);
}
template <typename T, typename... Args>
std::string join(const T& first, const Args& ...args) {
std::string final_string;
final_string.reserve(accumulated_size(first, args...));
append(final_string, first, args...);
return std::move(final_string);
}
Я протестировал метод join
в сравнении с типичными встроенными функциями конкатенации C++, используя operator+=
, а также operator+
класса std::string
на довольно большом количестве строк. Как и почему мой метод дает худшие результаты с точки зрения времени выполнения по сравнению с простым подходом operator+=
или operator+
?
Я использую следующий класс для измерения времени:
class timer {
public:
timer() {
start_ = std::chrono::high_resolution_clock::now();
}
~timer() {
end_ = std::chrono::high_resolution_clock::now();
std::cout << "Execution time: " << std::chrono::duration_cast<std::chrono::nanoseconds>(end_ - start_).count() << " ns." << std::endl;
}
private:
std::chrono::time_point<std::chrono::high_resolution_clock> start_;
std::chrono::time_point<std::chrono::high_resolution_clock> end_;
};
Я сравниваю следующим образом:
#define TEST_DATA "Lorem", "ipsum", "dolor", "sit", "ame", "consectetuer", "adipiscing", "eli", "Aenean",\
"commodo", "ligula", "eget", "dolo", "Aenean", "mass", "Cum", "sociis", "natoque",\
"penatibus", "et", "magnis", "dis", "parturient", "monte", "nascetur", "ridiculus",\
"mu", "Donec", "quam", "feli", ", ultricies", "ne", "pellentesque", "e", "pretium",\
"qui", "se", "Nulla", "consequat", "massa", "quis", "eni", "Donec", "pede", "just",\
"fringilla", "ve", "aliquet", "ne", "vulputate", "ege", "arc", "In", "enim", "just",\
"rhoncus", "u", "imperdiet", "", "venenatis", "vita", "just", "Nullam", "ictum",\
"felis", "eu", "pede", "mollis", "pretiu", "Integer", "tincidunt"
#define TEST_DATA_2 std::string("Lorem") + "ipsum"+ "dolor"+ "sit"+ "ame"+ "consectetuer"+ "adipiscing"+ "eli"+ "Aenean"+\
"commodo"+ "ligula"+ "eget"+ "dolo"+ "Aenean"+ "mass"+ "Cum"+ "sociis"+ "natoque"+\
"penatibus"+ "et"+ "magnis"+ "dis"+ "parturient"+ "monte"+ "nascetur"+ "ridiculus"+\
"mu"+ "Donec"+ "quam"+ "feli"+ ", ultricies"+ "ne"+ "pellentesque"+ "e"+ "pretium"+\
"qui"+ "se"+ "Nulla"+ "consequat"+ "massa"+ "quis"+ "eni"+ "Donec"+ "pede"+ "just"+\
"fringilla"+ "ve"+ "aliquet"+ "ne"+ "vulputate"+ "ege"+ "arc"+ "In"+ "enim"+ "just"+\
"rhoncus"+ "u"+ "imperdiet"+ ""+ "venenatis"+ "vita"+ "just"+ "Nullam"+ "ictum"+\
"felis"+ "eu"+ "pede"+ "mollis"+ "pretiu"+ "Integer"+ "tincidunt"
int main() {
std::string string_builder_result;
std::string normal_approach_result_1;
std::string normal_approach_result_2;
{
timer t;
string_builder_result = join(TEST_DATA);
}
std::vector<std::string> vec { TEST_DATA };
{
timer t;
for (const auto & x : vec) {
normal_approach_result_1 += x;
}
}
{
timer t;
normal_approach_result_2 = TEST_DATA_2;
}
}
Мои результаты:
- Время выполнения: 11552 нс (
join
подход). - Время выполнения: 3701 нс (
operator+=()
подход). - Время выполнения: 5898 нс (подход
operator+()
).
Я компилирую с: g++ efficient_string_concatenation.cpp -std=c++11 -O3
std::strlen
, что также занимает больше времени. странно, что это также работает хуже, чем альтернатива, использующаяoperator+()
из std::string, которая делает много распределений - person cuv   schedule 06.09.2017normal_approach_result_1
вы переместили все вызовыstrlen
за пределы таймера, тогда как два других подхода включают их в измерение. Такжеoperator+
в вашем случае не менее эффективен, чемoperator+=
. Есть еще один экспоненциально растущий буфер, в который аккумулируются все строки; этот буфер передается из временного во временный посредством перемещения. Так что я думаю, что это в основном сводится кstrlen
- самый быстрый подход не делает их, второй самый быстрый делает их один раз, самый медленный делает их дважды. - person Igor Tandetnik   schedule 06.09.2017