【发布时间】:2020-04-20 10:11:52
【问题描述】:
免责声明:这个问题更多是理论上的,而不是实际的兴趣。我想找出各种不同的方法来做这件事,速度就像新年蛋糕上的糖霜。
问题
我希望能够存储一个字符串列表,并能够在需要时将它们快速组合为 1。
简而言之,我想浓缩一个结构(目前是std::vector<std::string>),看起来像
["Hello, ", "good ", "day ", " to", " you!"]
到
["Hello, good day to you!"]
- 有没有什么惯用的方法来实现这一点,ala python 的
[ ''.join(list_of_strings) ]? - 就时间而言,在 C++ 中实现这一目标的最佳方法是什么?
可能的方法
我的第一个想法是
- 遍历向量,
- 将每个元素附加到第一个元素,
- 同时删除元素。
We will be concatenating with += and reserve()。我假设max_size() will not be reached。
方法 1(贪婪方法)
之所以这么称呼是因为它忽略了约定并就地运行。
#if APPROACH == 'G' // Greedy Approach void condense(std::vector< std::string >& my_strings, int total_characters_in_list) { // Reserve the size for all characters, less than max_size() my_strings[0].reserve(total_characters_in_list); // There are strings left, ... for(auto itr = my_strings.begin()+1; itr != my_strings.end();) { // append, and... my_strings[0] += *itr; // delete, until... itr = my_strings.erase(itr); } } #endif现在我知道了,你会说这很冒险而且很糟糕。所以:
- 循环遍历向量,
- 将每个元素附加到另一个
std::string, - 清除向量并使字符串成为向量的第一个元素。
方法 2(“安全”避风港)
之所以这么称呼,是因为它在迭代容器时不会修改容器。
#if APPROACH == 'H' // Safe Haven Approach void condense(std::vector< std::string >& my_strings, int total_characters_in_list) { // Store the whole vector here std::string condensed_string; condensed_string.reserve(total_characters_in_list); // There are strings left... for(auto itr = my_strings.begin(); itr != my_strings.end(); ++itr) { // append, until... condensed_string += *itr; } // remove all elements except the first my_strings.resize(1); // and set it to condensed_string my_strings[0] = condensed_string; } #endif现在是标准算法...
使用来自<algorithm>的std::accumulate
方法 3(成语?)
之所以这么叫,是因为它是单线的。
#if APPROACH == 'A' // Accumulate Approach void condense(std::vector< std::string >& my_strings, int total_characters_in_list) { // Reserve the size for all characters, less than max_size() my_strings[0].reserve(total_characters_in_list); // Accumulate all the strings my_strings[0] = std::accumulate(my_strings.begin(), my_strings.end(), std::string("")); // And resize my_strings.resize(1); } #endif为什么不尝试将其全部存储在流中?
使用来自<sstream> 的std::stringstream。
方法 4(字符串流)
之所以这样称呼,是因为 C++ 的流与水流的类比。
#if APPROACH == 'S' // Stringstream Approach void condense(std::vector< std::string >& my_strings, int) // you can remove the int { // Create out stream std::stringstream buffer(my_strings[0]); // There are strings left, ... for(auto itr = my_strings.begin(); itr != my_strings.end(); ++itr) { // add until... buffer << *itr; } // resize and assign my_strings.resize(1); my_strings[0] = buffer.str(); } #endif但是,也许我们可以使用另一个容器而不是
std::vector?
那么,还有什么?
(可能)方法 5(伟大的印度“绳索”技巧)
我听说过rope data structure,但不知道是否(以及如何)可以在这里使用它。
基准和判断:
按他们的时间效率排序(目前和令人惊讶的)是1:
Approaches Vector Size: 40 Vector Size: 1600 Vector Size: 64000
SAFE_HAVEN: 0.1307962699997006 0.12057728999934625 0.14202970000042114
STREAM_OF_STRINGS: 0.12656566000077873 0.12249500000034459 0.14765803999907803
ACCUMULATE_WEALTH: 0.11375975999981165 0.12984520999889354 3.748660090001067
GREEDY_APPROACH: 0.12164988000004087 0.13558526000124402 22.6994204800023
与2:
NUM_OF_ITERATIONS = 100
test_cases = [ 'greedy_approach', 'safe_haven' ]
for approach in test_cases:
time_taken = timeit.timeit(
f'system("{approach + ".exe"}")',
'from os import system',
number = NUM_OF_ITERATIONS
)
print(approach + ": ", time_taken / NUM_OF_ITERATIONS)
我们可以做得更好吗?
更新:我用 4 种方法测试了它(到目前为止),因为我可以在我的小时间里做到这一点。更多即将到来。折叠代码会更好,这样可以在这篇文章中添加更多方法,但是it was declined。
1 请注意,这些读数仅用于粗略估计。有a lot of things that influence the execution time,注意这里也有一些不一致的地方。
2 这是旧代码,仅用于测试前两种方法。当前的代码更长,而且集成度更高,所以我不确定是否应该在此处添加。
结论:
- 删除元素的成本很高。
- 您应该将字符串复制到某处,然后调整向量的大小。
- 事实上,如果复制到另一个字符串,最好也保留足够的空间。
【问题讨论】:
-
我将始终使用标准算法方法,如Combining a vector of strings 的答案中所述。只有当这真的是一个瓶颈时,我才会尝试优化它。但我不认为你可以在那里做很多关于性能的事情,所以我会选择最易读的一个。
-
@t.niese 有趣。让我对它进行基准测试。任何其他想法也将不胜感激。
-
在创建基准时始终需要小心。它们不一定反映这些方法在实际应用中的表现。大多数情况下,只有在实际应用程序中进行基准测试才有意义,但即便如此,也会有很多副作用,影响结果,因为这些副作用不容易正确解释。
-
环境变量、链接库、启动路径……所有这些都会影响内存布局和缓存,因此会对性能产生很大影响。因此,这不仅会影响这些基准,还会影响实际用例。这是一个完整的讨论:"Performance Matters" by Emery Berger
-
您对
std::vector::erase的使用会调用未定义的行为。将其返回值分配给itr以解决此问题。
标签: c++ string performance time