转换大约 150mb 的字符串的快速方法

Fast method to transform a string with about 150mb

本文关键字:方法 字符串 转换 150mb      更新时间:2023-10-16

我一直在尝试将std::stringstream中的每个char值减少100

std::string str = stream.str();
auto decrement = [](char c) { return c - 100; };
std::string out;
out.reserve(str.size());
std::transform(str.begin(), str.end(), std::back_inserter(out), decrement);
stream = std::stringstream(out);

但是花了7分钟的时间坚持std::transform指令。对于 150mb 的文本文件。

我没有使用优化的版本。这是调试版本。目标是能够使代码更快地运行以进行调试。对于此问题,发布结果是次要的。

关于如何提高效率的任何建议?

如果您不使用str进行其他任何操作,我会考虑的一件事是将其转换为原地。这样,您就可以写回从中读取的同一位置,并且可能会获得更好的缓存行为。只需更改

std::transform(str.begin(), str.end(), std::back_inserter(out), decrement);

std::transform(str.begin(), str.end(), str.begin(), decrement);

你可以完全摆脱你的out字符串。第三个(目标)参数允许与第一个参数相同。

这不仅完全摆脱了额外的 150MB 字符串变量,而且您以前必须访问内存中应该相距甚远的两个不同位置。通过读取和写回同一位置,您可以确保确实最大限度地利用了缓存。

当然,这会str变异,所以只有当你不需要原始的str变量做其他事情时,它才真正有用。

最终结果:

std::string str = stream.str();
auto decrement = [](char c) { return c - 100; };
std::transform(str.begin(), str.end(), str.begin(), decrement);
stream = std::stringstream(str);

有两个明显的加速。

首先是就地转换。

std::string str = stream.str();
auto decrement = [=](char c) { return c -= 100; };
std::transform(str.begin(), str.end(), str.begin(), decrement);
stream = std::stringstream(str);

被拉斐尔覆盖。

第二个,只是因为你想要 DEBUG 优化的速度,是绕过可能的调试迭代器检查。

std::string str = stream.str();
auto decrement = [=](char c) { return c -= 100; };
std::transform(&str[0], (&str[0])+str.size(), (&str[0]), decrement);
stream = std::stringstream(str);

在这里,我们将begin()替换为 &str[0] ,一个指向字符缓冲区内容的原始指针。 如果您正在使用非常奇怪的basic_string,请使用 std::addressof 而不是 &

在具有调试检测的迭代器的系统中,这可能会快得多。 在优化的版本中,我希望它的速度相同。

稍微

不那么优雅,但我认为仍然可以接受(也取决于您的目标机器)如果您需要额外的速度(比 Raphael 提供的解决方案快约 5 倍),请使用 sse 内联函数 (SSE2)。

#include <emmintrin.h>
__m128i dec = _mm_set1_epi8(100);
size_t x = 0;
for (; x < str.size()-15; x+=16)
{
    __m128i sse = _mm_loadu_si128((__m128i*)&str[x]);
    _mm_storeu_si128((__m128i*)&str[x], _mm_sub_epi8(sse, dec));
}
for (; x < str.size(); ++x)
    str[x] -= 100;