Более быстрая альтернатива std :: ofstream - PullRequest
1 голос
/ 08 июня 2011

Я генерирую набор файлов данных.Поскольку файлы должны быть читаемыми, они текстовые файлы (в отличие от двоичных файлов).

Для вывода информации в мои файлы я использовал очень удобный объект std :: ofstream .

В начале, когда экспортируемые данные были меньше, время, необходимое для записи в файлы, не было заметно.Однако, поскольку экспортируемая информация накапливается, теперь около 5 минут их генерируется.

Поскольку я начал беспокоиться, ожидая , мой вопросочевидно: Есть ли более быстрая альтернатива std :: ofstream , пожалуйста?Если есть более быстрая альтернатива, стоит ли переписывать мое приложение?Другими словами, может ли сэкономленное время быть + 50% ?Спасибо.


Обновление:

Меня попросили показать мой код , который генерирует вышеуказанные файлы, так что вы здесь - наиболеедлительный цикл :

ofstream fout;
fout.open(strngCollectiveSourceFileName,ios::out);

fout << "#include \"StdAfx.h\"" << endl;
fout << "#include \"Debug.h\"" << endl;
fout << "#include \"glm.hpp\"" << endl;
fout << "#include \"" << strngCollectiveHeaderFileName.substr( strngCollectiveHeaderFileName.rfind(TEXT("\\")) + 1) << "\"" << endl << endl;

fout << "using namespace glm;" << endl << endl << endl;


for (unsigned int nSprite = 0; nSprite < vpTilesetSprites.size(); nSprite++ )
{
    for(unsigned int nFrameSet = 0; nFrameSet < vpTilesetSprites[nSprite]->vpFrameSets.size(); nFrameSet++)
    {

        // display index definition
        fout << "// Index Definition: " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetLongDescription() << "\n";
        string strngIndexSignature = strngIndexDefinitionSignature;
        strngIndexSignature.replace(strngIndexSignature.find(TEXT("#aIndexArrayName#")), strlen(TEXT("#aIndexArrayName#")), TEXT("a") + vpTilesetSprites[nSprite]->GetObjectName() + vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFrameSetName() + TEXT("IndexData") );
        strngIndexSignature.replace(strngIndexSignature.find(TEXT("#ClassName#")), strlen(TEXT("#ClassName#")), strngCollectiveArrayClassName );        
        fout << strngIndexSignature << "[4] = {0, 1, 2, 3};\t\t" << "// " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << ": Index Definition\n\n";


        // display vertex definition
        fout << "// Vertex Definition: " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetLongDescription() << "\n";

        string strngVertexSignature = strngVertexDefinitionSignature;
        strngVertexSignature.replace(strngVertexSignature.find(TEXT("#aVertexArrayName#")), strlen(TEXT("#aVertexArrayName#")), TEXT("a") + vpTilesetSprites[nSprite]->GetObjectName() + vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFrameSetName() + TEXT("VertexData") );
        strngVertexSignature.replace(strngVertexSignature.find(TEXT("#ClassName#")), strlen(TEXT("#ClassName#")), strngCollectiveArrayClassName );
        fout << strngVertexSignature << "[" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFramesCount() << "] =\n";
        fout << "{\n";

        for (int nFrameNo = 0; nFrameNo < vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFramesCount(); nFrameNo++)
        {
            fout << "\t" << "{{ vec4(" << fixed << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vPosition.fx << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vPosition.fy << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vPosition.fz << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vPosition.fw << "f), vec2(" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vTextureUV.fu << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vTextureUV.fv << "f) },  // " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << " vertex 1: vec4(x, y, z, w), vec2(u, v) \n";
            fout << "\t" << " { vec4(" << fixed << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vPosition.fx << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vPosition.fy << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vPosition.fz << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vPosition.fw << "f), vec2(" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vTextureUV.fu << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vTextureUV.fv << "f) },  // " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << " vertex 2: vec4(x, y, z, w), vec2(u, v) \n";
            fout << "\t" << " { vec4(" << fixed << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vPosition.fx << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vPosition.fy << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vPosition.fz << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vPosition.fw << "f), vec2(" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vTextureUV.fu << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vTextureUV.fv << "f) },  // " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << " vertex 3: vec4(x, y, z, w), vec2(u, v) \n";
            fout << "\t" << " { vec4(" << fixed << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vPosition.fx << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vPosition.fy << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vPosition.fz << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vPosition.fw << "f), vec2(" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vTextureUV.fu << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vTextureUV.fv << "f) }},  // " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << " vertex 4: vec4(x, y, z, w), vec2(u, v) \n\n";
        }
        fout << "};\n\n\n\n";
    }
}

fout.close();

Ответы [ 5 ]

4 голосов
/ 08 июня 2011

Если вы не хотите использовать C / I / O, вы можете попробовать; FastFormat .Посмотрите на сравнение для получения дополнительной информации.

3 голосов
/ 08 июня 2011

Как хранятся vpTilesetSprites и vpTilesetSprites[nSprite]? Они реализованы со списками или массивами? Индексированный доступ к ним имеет много , и если они являются списковыми структурами, вы потратите много дополнительного времени на ненужные указатели. Комментарий Эда С. прав: предоставление длинных индексированных временных переменных и разрывов строк может облегчить чтение, а также , возможно, быстрее:

fout << "// Index Definition: " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetLongDescription() << "\n";
string strngIndexSignature = strngIndexDefinitionSignature;
strngIndexSignature.replace(strngIndexSignature.find(TEXT("#aIndexArrayName#")), strlen(TEXT("#aIndexArrayName#")), TEXT("a") + vpTilesetSprites[nSprite]->GetObjectName() + vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFrameSetName() + TEXT("IndexData") );
strngIndexSignature.replace(strngIndexSignature.find(TEXT("#ClassName#")), strlen(TEXT("#ClassName#")), strngCollectiveArrayClassName );        

против

string idxsig = strngIndexDefinitionSignature;
sprite sp = vpTilesetSprites[nSprite];
frameset fs = sp->vpFrameSets[nFrameSet];

fout << "// Index Definition: " << fs->GetLongDescription() << "\n";
idxsig.replace(idxsig.find(TEXT("#aIndexArrayName#")), strlen(TEXT("#aIndexArrayName#")),
    TEXT("a") + sp->GetObjectName() + fs->getFrameSetName() + TEXT("IndexData"));
idxsig.replace(idxsig.find(TEXT("#ClassName#")), strlen(TEXT("#ClassName#")),
    strngCollectiveArrayClassName);

Но гораздо большая проблема заключается в том, как вы используете строки в качестве шаблонов; вы ищете заданную текстовую строку (и вычисляете длину вашей иглы строки каждый раз, когда вам это нужно!) снова и снова .

Обратите внимание: вы выполняете операции поиска и замены nSprite * nFrameSet раз. Каждый раз через этот цикл:

  • делает копию strngIndexDefinitionSignature
  • создает четыре временных строковых объекта при объединении статических и динамических строк
  • вычислить strlen(TEXT("#ClassName#"))
  • вычислить strlen(TEXT("#aIndexArrayName#"))
  • найти начальную точку обоих
  • заменить оба текста новыми текстами

И это только первые четыре строки вашего цикла.

Можете ли вы заменить strngIndexDefinitionSignature на строку формата ? Я предполагаю, что в настоящее время это выглядит так:

"flubber #aIndexArrayName# { blubber } #ClassName# blorp"

Если переписать это так:

"flubber a %s%sIndexData { blubber } %s blorp"

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

sprintf(out, index_def_sig, sp->GetObjectName(), fs->getFrameSetName(),
    strngCollectiveArrayClassName);

Это приведет к удалению двух find() операций, двух replace() операций, созданию и уничтожению четырех временных строковых объектов, дубликата строки, который был быстро перезаписан двумя replace() вызовами, и двух strlen() операций, которые возвращают каждый раз один и тот же результат (но на самом деле он не нужен).

Затем вы можете вывести свою строку с помощью << как обычно. Или вы можете изменить sprintf(3) на fprintf(3) и избежать даже временной строки C.

3 голосов
/ 08 июня 2011

Если вы делаете это достаточно большими кусками, прямой вызов write () может быть быстрее; Тем не менее, более вероятно, что ваше самое большое узкое место не имеет ничего общего с std :: ofstream. Наиболее очевидная вещь - убедиться, что вы не используете std :: endl (потому что частая очистка потока снижает производительность). Кроме того, я бы предложил профилировать ваше приложение, чтобы увидеть, где оно на самом деле тратит время.

1 голос
/ 08 июня 2011

Лучший ответ будет зависеть от того, какой текст вы генерируете, и как вы его генерируете. Потоки C ++ могут быть медленными, но это в основном потому, что они также могут сделать для вас гораздо больше, например, форматирование, зависящее от локали, и т. Д.

Вы можете найти ускорение с потоками, пропуская часть форматирования (например, ostream::write), или вместо этого записывая символы непосредственно в streambuf (streambuf::sputn). Иногда помогает увеличение размера буфера в соответствующем потоковом буфере (через streambuf::pubsetbuf).

Если этого недостаточно, возможно, вы захотите попробовать stdio-файлы в стиле C, например, fopen, fprintf и т. Д. Чтобы привыкнуть к форматированию текста, вам потребуется некоторое время. Вы не привыкли к этому методу, но производительность, как правило, довольно хорошая.

Для достижения максимальной производительности вы обычно должны переходить к процедурам, специфичным для ОС. Иногда прямые низкоуровневые файловые процедуры значительно лучше, чем C stdio, но иногда нет - например, я видел, как некоторые люди говорят, что WriteFile на Win32 - самый быстрый метод в Windows, тогда как некоторые хиты Google сообщают, что он медленнее, чем STDIO. Другим подходом может быть файл с отображением в памяти, например. mmap + msync - это, по сути, использует вашу системную память в качестве диска и записывает фактические данные на диск большими блоками, что, вероятно, будет почти оптимальным. Однако вы рискуете потерять все данные, если по какой-то причине произойдет сбой на полпути, что может быть или не быть проблемой для вас.

1 голос
/ 08 июня 2011

Производительность ostream, вероятно, не является вашей реальной проблемой; Я предлагаю использовать профилировщик, чтобы определить, где ваши настоящие узкие места. Если ostream окажется вашей реальной проблемой, вы можете перейти к <cstdio> и использовать fprintf(FILE*, const char*, ...) для форматированного вывода в дескриптор файла.

...