С++ stringstream слишком медленный, как ускорить?
Возможный дубликат:
Самый быстрый способ чтения числовых значений из текстового файла на С++ (в этом случае двойной)
#include <ctime>
#include <cstdlib>
#include <string>
#include <sstream>
#include <iostream>
#include <limits>
using namespace std;
static const double NAN_D = numeric_limits<double>::quiet_NaN();
void die(const char *msg, const char *info)
{
cerr << "** error: " << msg << " \"" << info << '\"';
exit(1);
}
double str2dou1(const string &str)
{
if (str.empty() || str[0]=='?') return NAN_D;
const char *c_str = str.c_str();
char *err;
double x = strtod(c_str, &err);
if (*err != 0) die("unrecognized numeric data", c_str);
return x;
}
static istringstream string_to_type_stream;
double str2dou2(const string &str)
{
if (str.empty() || str[0]=='?') return NAN_D;
string_to_type_stream.clear();
string_to_type_stream.str(str);
double x = 0.0;
if ((string_to_type_stream >> x).fail())
die("unrecognized numeric data", str.c_str());
return x;
}
int main()
{
string str("12345.6789");
clock_t tStart, tEnd;
cout << "strtod: ";
tStart=clock();
for (int i=0; i<1000000; ++i)
double x = str2dou1(str);
tEnd=clock();
cout << tEnd-tStart << endl;
cout << "sstream: ";
tStart=clock();
for (int i=0; i<1000000; ++i)
double x = str2dou2(str);
tEnd=clock();
cout << tEnd-tStart << endl;
return 0;
}
strtod: 405
sstream: 1389
update: удалить нижние символы, env: win7 + vc10
Ответы
Ответ 1
C/С++ текст для форматирования чисел очень медленный. Потоки ужасающе медленны, но даже синтаксический анализ числа C медленный, потому что довольно сложно получить его с точностью до последнего бита точности.
В производственном приложении, где скорость чтения была важна, и когда данные, как известно, имели не более трех десятичных цифр и не имели научной нотации, я получил значительное улучшение путем ручного кодирования функции плавающего разбора, обрабатывающей только знак, целочисленную часть и любое число из десятичных знаков ( "огромным" я имею в виду 10 раз быстрее, чем strtod
).
Если вам не нужен показатель экспоненты и точность этой функции достаточно, это код анализатора, аналогичный тому, который я написал тогда. На моем ПК он теперь в 6,8 раза быстрее, чем strtod и в 22,6 раза быстрее, чем sstream.
double parseFloat(const std::string& input)
{
const char *p = input.c_str();
if (!*p || *p == '?')
return NAN_D;
int s = 1;
while (*p == ' ') p++;
if (*p == '-') {
s = -1; p++;
}
double acc = 0;
while (*p >= '0' && *p <= '9')
acc = acc * 10 + *p++ - '0';
if (*p == '.') {
double k = 0.1;
p++;
while (*p >= '0' && *p <= '9') {
acc += (*p++ - '0') * k;
k *= 0.1;
}
}
if (*p) die("Invalid numeric format");
return s * acc;
}
Ответ 2
Строковый поток медленный. Довольно очень медленно. Если вы пишете что-то критичное по производительности, которое действует на больших наборах данных (например, загружать активы после изменения уровня во время игры), не используйте строковые потоки. Я рекомендую использовать функции синтаксического анализа старой библиотеки c для производительности, хотя я не могу сказать, как они сравниваются с чем-то вроде boost spirit.
Однако, по сравнению с функциями библиотеки c, потоки строк очень элегантны, читабельны и надежны, поэтому, если вы делаете не производительность ciritcal, я рекомендую придерживаться потоков.
Ответ 3
В общем, если вам нужна скорость, рассмотрите эту библиотеку:
http://www.fastformat.org/
(Я не уверен, что он содержит функции для преобразования строк или потоков в другие типы, поэтому, возможно, он не может ответить на ваш текущий пример).
Для записи обратите внимание, что вы сравниваете яблоки с апельсинами здесь. strtod()
- простая функция, которая имеет одну цель (преобразование строк в double), в то время как stringstream - гораздо более сложный механизм форматирования, который далеко не оптимизирован для этой конкретной цели. Более справедливым сравнением будет сравнение stringstream со строкой sprintf/sscanf, которая будет медленнее, чем strtod()
, но все же быстрее, чем stringstream. Я не совсем уверен, что делает конструкцию строк более медленной, чем sprintf/sscanf, но это похоже на случай.
Ответ 4
Рассматривали ли вы использование lexical_cast
из boost?
http://www.boost.org/doc/libs/1_46_1/libs/conversion/lexical_cast.htm
Изменить: btw, clear()
должен быть избыточным.