如果转换是瓶颈(这很有可能),您应该从使用标准中的不同可能性开始。从逻辑上讲,人们会期望它们非常接近,但实际上,它们并不总是:
你已经确定这std::ifstream
太慢了。
将内存映射数据转换为一个std::istringstream
几乎肯定不是一个好的解决方案;您首先必须创建一个字符串,它将复制所有数据。
编写自己的streambuf
直接从内存中读取,而不复制(或使用不推荐使用的std::istrstream
)可能是一个解决方案,尽管如果问题真的是转换......这仍然使用相同的转换例程。
您可以随时尝试fscanf
,或scanf
在您的内存映射流上。根据实现,它们可能比各种istream
实现更快。
可能比其中任何一个都快使用strtod
. 无需为此进行标记:strtod
跳过前导空格(包括'\n'
),并有一个 out 参数,它将未读取的第一个字符的地址放置在其中。结束条件有点棘手,您的循环可能看起来有点像:
char* 开始;// 设置为指向 mmap'ed 数据...
// 你还必须安排一个 '\0'
// 跟随数据。这大概是
// 最困难的问题。
字符*结束;
错误号 = 0;
双 tmp = strtod(开始,&结束);
while ( errno == 0 && end != begin ) {
// 用 tmp 做任何事情...
开始=结束;
tmp = strtod(开始,&结束);
}
如果这些都不够快,您将不得不考虑实际数据。它可能有某种额外的约束,这意味着您可能会编写一个比更通用的转换例程更快的转换例程;例如strtod
,必须同时处理固定和科学,即使有 17 位有效数字,它也必须 100% 准确。它还必须是特定于语言环境的。所有这些都增加了复杂性,这意味着添加了要执行的代码。但请注意:编写一个有效且正确的转换例程,即使对于一组受限的输入,也并非易事;你真的必须知道你在做什么。
编辑:
只是出于好奇,我进行了一些测试。除了前面提到的解决方案,我写了一个简单的自定义转换器,它只处理定点(不科学),小数点后最多五位,小数点前的值必须适合int
:
double
convert( char const* source, char const** endPtr )
{
char* end;
int left = strtol( source, &end, 10 );
double results = left;
if ( *end == '.' ) {
char* start = end + 1;
int right = strtol( start, &end, 10 );
static double const fracMult[]
= { 0.0, 0.1, 0.01, 0.001, 0.0001, 0.00001 };
results += right * fracMult[ end - start ];
}
if ( endPtr != nullptr ) {
*endPtr = end;
}
return results;
}
(如果你真的使用它,你肯定应该添加一些错误处理。这只是出于实验目的而快速敲掉,以读取我生成的测试文件,仅此而已
。)
该接口正是 的接口strtod
,以简化编码。
我在两个环境中运行了基准测试(在不同的机器上,所以任何时间的绝对值都不相关)。我得到以下结果:
在 Windows 7 下,使用 VC 11 (/O2) 编译:
Testing Using fstream directly (5 iterations)...
6.3528e+006 microseconds per iteration
Testing Using fscan directly (5 iterations)...
685800 microseconds per iteration
Testing Using strtod (5 iterations)...
597000 microseconds per iteration
Testing Using manual (5 iterations)...
269600 microseconds per iteration
在 Linux 2.6.18 下,使用 g++ 4.4.2 (-O2, IIRC) 编译:
Testing Using fstream directly (5 iterations)...
784000 microseconds per iteration
Testing Using fscanf directly (5 iterations)...
526000 microseconds per iteration
Testing Using strtod (5 iterations)...
382000 microseconds per iteration
Testing Using strtof (5 iterations)...
360000 microseconds per iteration
Testing Using manual (5 iterations)...
186000 microseconds per iteration
在所有情况下,我都在读取 554000 行,每行在 range 中有 3 个随机生成的浮点[0...10000)
。
fstream
最引人注目的是 Windows和Windows 下的巨大差异
(以及和fscan
之间的相对较小的差异)。第二件事是简单的自定义转换功能在两个平台上获得了多少收益。必要的错误处理会稍微减慢它的速度,但差异仍然很大。我预计会有一些改进,因为它不能处理标准转换例程所做的很多事情(比如科学格式、非常非常小的数字、Inf 和 NaN、i18n 等),但不是那么多。fscan
strtod