быстрее, чем fopen?и становится быстрее с увеличением буферов? - PullRequest
0 голосов
/ 06 июля 2019

Я пытался записать данные файла как можно быстрее.

  • Я увеличил размер буфера, чтобы уменьшить количество операций ввода-вывода.
  • Я протестировалс fstream и fopen.

По какой-то причине fstream быстрее, чем fopen.

  • в 64-байтовом буфере, он в ~ 1,3 раза быстрее
  • вкл.8192-байтовый буфер работает в ~ 4,8 раза.

Я слышал, что файловый ввод / вывод C быстрее (что имеет смысл)
<fstream> включает <stdio.h>, но я могуfopen не работает так быстро.

ПРИМЕЧАНИЕ (старые вопросы):

  • мой fopen был в 2 раза медленнее fstream, потому что я использовал fprintf (спасибо jamesdlin )
  • буфер fstream не изменился, так как вы должны установить его перед открытием (спасибо Пол Сандерс )

также реализован fstream.put (char)быстрее, чем fstream << char <br>(иначе fopen быстрее, чем fstream, если буфер <~ 256) </p>

Вот мое тестирование:

#include <iostream>
#include <fstream>
#include <ctime>

int filesize; // total bytes (individually "put" in buffered stream)
int buffsize; // buffer size

void writeCPP(){
    std::ofstream file;
    char buffer[buffsize]; file.rdbuf()->pubsetbuf(buffer,buffsize);    // set buffer (before opening)
    file.open("test.txt",std::ios::binary);                             // open file
    for(int i=0; i<filesize; i++) file.put('a');                        // write bytes
    file.close();                                                       // close
}

void writeC(){
    FILE* file=fopen("test.txt","wb");                                  // open file
    char buffer[buffsize]; setvbuf(file,buffer,_IOFBF,buffsize);        // set buffer
    for(int i=0; i<filesize; i++) fputc('a',file);                      // write bytes
    fclose(file);                                                       // close
}

#define getTime() double(clock())/CLOCKS_PER_SEC // good enough

double start;

void test(int s){ // C++ vs C (same filesize / buffsize)
    buffsize=s;
    std::cout<<"  buffer: "<<buffsize<<"\t"<<std::flush;

    start=getTime();
    writeCPP();
    std::cout<<"  C++: "<<getTime()-start<<",\t"<<std::flush;

    start=getTime();
    writeC();
    std::cout<<" C: "<<getTime()-start<<std::endl;
}

#define MB (1024*1024)

int main(){
    filesize=10*MB;
    std::cout<<"size: 10 MB"<<std::endl;

    // C++ fstream faster
    test(64);   // C++ 0.86 < C 1.11 (1.29x faster)
    test(128);  // C++ 0.44 < C 0.79 (1.80x faster) (+0.51x)
    test(256);  // C++ 0.27 < C 0.63 (2.33x faster) (+0.53x)
    test(512);  // C++ 0.19 < C 0.56 (2.94x faster) (+0.61x)
    test(1024); // C++ 0.15 < C 0.52 (3.46x faster) (+0.52x)
    test(2048); // C++ 0.14 < C 0.51 (3.64x faster) (+0.18x)
    test(4096); // C++ 0.12 < C 0.49 (4.08x faster) (+0.44x)
    test(8192); // C++ 0.10 < C 0.48 (4.80x faster) (+0.72x)
}

Ответы [ 3 ]

2 голосов
/ 06 июля 2019

В WriteCPP необходимо установить буфер перед открытием файла, например:

std::ofstream file;
char buffer[BUFF]; file.rdbuf()->pubsetbuf(buffer, BUFF);   // set buffer
file.open ("test.txt", std::ios::binary);                   // open file

Затем вы получите результаты, которые вы можете ожидать (время для записи 20 МБ с указанным размером буфера):

writeCPP, 32: 2.15278
writeCPP, 128: 1.21372
writeCPP, 512: 0.857389

Я также сравнил WriteC с вашим изменением с fprintf на fputc и получил следующее (снова записав 20 МБ):

writeC, 32: 1.41433
writeC, 128: 0.524264
writeC, 512: 0.355097

Тестовая программа здесь:

https://wandbox.org/permlink/F2H2jcrMVsc5VNFf

2 голосов
/ 06 июля 2019

fprintf имеет дополнительные издержки, так как ему нужно сканировать входную строку на предмет спецификаторов формата, так что вы не совсем делаете сравнение яблок с яблоками.

Лучшим сравнением будет использование fputs вместо fprintf или использовать fputc, а затем использовать file << 'a' в версии iostream.

0 голосов
/ 06 июля 2019

Единственное стандартное поведение для std::basic_filebuf::setbuf состоит в том, что setbuf(0, 0) устанавливает поток на небуферизованный вывод, и даже в этом случае я не буду рассчитывать на него.

фактический Поведение setbuf сильно отличается от реализации к реализации:

  • libstdc ++: setbuf работает только при вызове до открытия файла.Помимо этого, он делает то, что вы, вероятно, ожидаете.Каждый раз, когда буфер заполняется, вы получаете один вызов базового системного вызова write.
  • libc ++: setbuf можно вызывать после открытия файла, но до того, как будет выполнен какой-либо ввод-вывод.Каждый раз, когда буфер заполняется, вы получаете вызов fwrite на базовом FILE*.Это означает, что вывод по-прежнему буферизуется с использованием внутреннего буфера FILE.Нет никакого способа получить доступ к этому внутреннему FILE* к setbuf или setvbuf, поэтому вы застряли с размером буфера по умолчанию (в настоящее время 4096 байт, по крайней мере, в реализации glibc).
  • MSVCRT: basic_filebuf делит свои буферы с базовым объектом FILE.setbuf просто передает буфер, который вы передаете, вызову setvbuf на базовом FILE*.Может быть вызван в любое время, но удалит все ранее буферизованные данные.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...