C++ 快速转换一个大约150mb字符串的方法

C++ 快速转换一个大约150mb字符串的方法,c++,performance,c++11,C++,Performance,C++11,我一直试图将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
中的每个
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的文本文件

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


关于如何提高效率,有什么建议?

< P>我想,如果你不使用你的<代码> STR <代码>,我会考虑把它转化为其他的东西。这样,您就可以回写到读取的相同位置,并可能获得更好的缓存行为。简单地改变

std::transform(str.begin(), str.end(), std::back_inserter(out), 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);
这是拉斐尔报道的

#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;
第二种方法是绕过可能的调试迭代器检查,这只是因为您希望调试优化速度

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_字符串
s,请使用
std::addressof
而不是
&


在一个迭代器充斥着调试工具的系统中,这可能要快得多。在优化的构建中,我希望它的速度是相同的。

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

#包括
__m128i dec=_mm_set1_epi8(100);
尺寸x=0;
对于(;x
另外,在
out
中预先保留必要的空间不会提高性能吗?一定有一些东西您没有显示出来,因为我无法复制您的结果。对于170MB的文本文件,转换需要3.3秒(如果I
reserve()
,则需要2.7秒)。您确定是转换部分花费了时间,而不是开始和结束时的流式输入/输出吗?@AlexandreSeverino如果您使用的是Visual Studio,那么代码未优化的原因是调用STL函数时会进行大量调试检查、迭代器验证等。从这样的构建发布计时是完全没有意义的。@PaulMcKenzie我不同意:能够在调试中使用您的代码库是至关重要的,而仅仅因为一个很窄的位在调试中太慢而使所有内容都得到优化(并且更难调试)会使调试构建变得不那么有用。调试过程中速度非常慢是一个严重的问题。但一旦事情“足够快”到可以运行,获得最后一点速度(在调试中)比在发布中更重要。@Yakk如果要更改的代码不太引人注目,这可能是可能的。但是想象一下,如果您正在使用,比如说一个
std::map
,您会发现它在调试中很慢。这是否意味着您使用它转储并编写一个自制的、可能有bug的映射,只是为了满足调试目的?假设
map
在发行版中运行时,其性能确实优于自制的替代产品?这就是仅仅为了满足调试需求而更改代码的危险。就地编辑听起来是一个不错的尝试。会给你机会的。轰!工作完美。代码运行不到3秒!!!现在感觉很明显。。。我建议编辑并添加一个进一步的解释,说明为什么更改字符串比较好(避免复制和其他东西)。如果可能的话,最好是将数据转换成
流。@RaphaelMiedl:这取决于他的数据流。数据必须以某种方式进入流中,首先,在输入字符的过程中转换字符可能很方便:从流的生命周期开始就进行了转换,您根本不需要新的字符串或流。但这可能不适合他的申请;我们只是不知道。nits:您不必捕获lambda中的任何内容,只需简单的
[]
即可,您还可以返回
c-100
,而不是
c-=100
。您可能还需要在该字符串缓冲区上使用alignas(16),以便能够使用对齐版本的加载。这可能意味着您必须手动分配数组,而不是使用std::string(或使用其他一些SIMD友好的容器),这既有趣又有用,但我不需要太多的性能提升。C++11保证
data()
C_str()
返回一个指向
字符串所使用的内部数组的指针,因此我们可以使用它们中的任何一个来代替
&str[0]
.Ah、
data()
c_str()
返回
常量字符*
,因此我们只能使用它们来指定输入范围。
#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;