stringstream сливает :(

Не то чтобы я большой любитель C++, тем не менее всегда считал что форматирование через stringstream работает быстрее чем форматирование через sprintf. В очередной раз обсудив плюсы и минусы форматирования с коллегами, решили что проще “достать да померять”. Итог оказался довольно неожиданный для меня: stringstream позорно слил в лучшем случае в 2 раза, а в худшем в 3!
Вариант на C:

#include <sys/time.h>
#include <stdio.h>

int main()
{
    int a = 256;
    int b = 0xbaadf00d;
    float c = 3.1415926535;
    char d[] = "17 symbols string";
   
    int i;

    struct timeval time;
    gettimeofday(&time, NULL);

    for (i = 0; i < 0x100000; ++i)
    {
        char buffer[1024];
        sprintf(buffer, "%010d %X %+*.*f %.5s", a, b, 3, 2, c, d);
    }

    struct timeval time1;
    gettimeofday(&time1, NULL);
    printf("%i", ((time1.tv_sec * 1000) + (time1.tv_usec / 1000)) - ((time.tv_sec * 1000) + (time.tv_usec / 1000)));
}

Вариант на C++:

#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <sys/time.h>

using namespace std;

int main()
{
    int a = 256;
    int b = 0xbaadf00d;
    float c = 3.1415926535;
    char d[] = "17 symbols string";

    timeval time;
    gettimeofday(&time, NULL);

    for (i = 0; i < 0x100000; ++i)
    {
        stringstream stream;
        stream << setfill('0') << setw(10) << a << " "
            << hex << uppercase << b << " "
            << showpos << setprecision(3) << c << " "
            << string(d, d+5);
    }
    timeval time1;
    gettimeofday(&time1, NULL);

    cout << ((time1.tv_sec * 1000) + (time1.tv_usec / 1000)) - ((time.tv_sec * 1000) + (time.tv_usec / 1000));
}

8 Comments stringstream сливает :(

  1. hmich

    Твоя программа ускоряется в два раза если не создавать тяжелые объекты на каждой итерации цикла:

    string str( d, d + 5 );
    ostringstream stream;
    stream.precision(3);
    stream.fill('0');
    for (int i = 0; i < 0x100000; ++i)
    {
    stream << setw(10) << a << " "
    << hex << uppercase << b << " "
    << showpos << c << " " << str;
    stream.str("");
    }

    Reply
  2. Alexander Stavonin

    тогда эксперимент будет не чистым. дело в том, что была попытка сравнить скорость в условиях приближенных к реальным. Ну а в реале ты явно не будешь создавать единственный объект класса stringstream и организовывать к нему доступ из всех частей программы

    Reply
  3. NN​

    stringstream медленный. это факт.
    На самом деле его можно ускорить, но в реализации MS VC с этим не заморачиваются.

    Я как-то смотрел, там в общем, имеет место аллокации в ненужных местах. Которые вполне можно заменить на аллокацию на стеке: boost::optional / placement new .

    Потом совсем дикий способ форматирования, видимо из-за требований к стандарту..

    В итоге чуть ли не на каждое число вызываем new, и передаем в sprintf

    Если нужна скорость, и не нужен поток.
    http://www.fastformat.org или sprintf.

    С другой стороны, у каждого разные возможности, поэтому нужно оценивать что важнее.

    Reply
  4. Alexander Stavonin

    Я тут поглядел примеры на http://www.fastformat.org и они меня несколько разочаровали. Такое ощущение, что все хорошо пока тебе надо вывести значение в представлении "по умолчанию". А вот повторить то, что у меня в примере выше ну никак не представляется возможным.
    Зато инфа про буст порадовала – всегда считал их реализацию тормозным монстром

    Reply
  5. NN​

    Да, boost::format это тормоз еще тот.
    Зато он умеет очень много.

    Ну вот и пришли.
    Либо выбираем скорость либо фичи.

    Кстати, самый лучший вариант это какой-нибудь compile time printf.

    Типа
    mysprintf< описываем строку > ( передаем аргументы )

    Тогда и проверка типов и можно сразу sprintf с нужными аргументами вызвать.

    Reply
  6. Alexander Stavonin

    так по сочетанию скорость/фичи sprintf всех рвет как тузик грелку

    Reply
  7. NN​

    У него нет другой расширяемости.
    Нельзя boost::tuple вывести в sprintf с такой же легкостью как в ostream.

    Reply
  8. WM

    Нубское сравнение какое-то.
    Две абсолютные разные системы вывода.
    Для начала выделяйте буфер динамически. Во-вторых не забываем что потоки работают с объектами локалей. В третьих принтф не расширяемое и не безопасное решение, о каких фитчах речь?

    boost::format универсальное решение, объединяющее оба интерфейса, но как и любое универсальное решение медленее обоих.

    Reply

Leave a Reply