#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>

using namespace std;

vector<char> f1()
{
    ifstream fin{ "input.txt", ios::binary };
    return
    {
        istreambuf_iterator<char>(fin),
        istreambuf_iterator<char>()
    };
}

vector<char> f2()
{
    vector<char> coll;
    ifstream fin{ "input.txt", ios::binary };
    char buf[1024];
    while (fin.read(buf, sizeof(buf)))
    {
        copy(begin(buf), end(buf),
            back_inserter(coll));
    }

    copy(begin(buf), begin(buf) + fin.gcount(),
        back_inserter(coll));

    return coll;
}

int main()
{
    f1();
    f2();
}

Obviamente, f1() es más conciso que f2(); así que prefiero f1() a f2(). Sin embargo, me preocupa que f1() sea menos eficiente que f2().

Entonces, mi pregunta es:

¿Los compiladores principales de C ++ optimizarán f1() para hacerlo tan rápido como f2()?

Actualización:

He usado un archivo de 130M para probar en modo de lanzamiento (Visual Studio 2015 con Clang 3.8):

f1() tarda 1614 ms, mientras que f2() tarda 616 ms.

f2() es más rápido que f1().

¡Qué triste resultado!

1
xmllmx 14 dic. 2016 a las 13:04

2 respuestas

La mejor respuesta

He comprobado tu código de mi lado usando mingw482. Por curiosidad, agregué una función adicional f3 con la siguiente implementación:

inline vector<char> f3()
{
    ifstream fin{ filepath, ios::binary };
    fin.seekg (0, fin.end);
    size_t len = fin.tellg();
    fin.seekg (0, fin.beg);

    vector<char> coll(len);
    fin.read(coll.data(), len);
    return coll;
}

Probé usando un archivo de ~90M de largo. Para mi plataforma, los resultados fueron un poco diferentes a los tuyos.

  • f1 () ~ 850 ms
  • f2 () ~ 600 ms
  • f3 () ~ 70 ms

Los resultados se calcularon como la media de 10 lecturas consecutivas de archivos.

La función f3 toma menos tiempo ya que en vector<char> coll(len); tiene toda la memoria requerida asignada y no es necesario realizar más reasignaciones. En cuanto a back_inserter, requiere que el tipo tenga una función de miembro push_back . Que para vector hace la reasignación cuando se excede capacity. Como se describe en los documentos:

retroceso

Esto aumenta efectivamente el tamaño del contenedor en uno, lo que provoca una reasignación automática del espacio de almacenamiento asignado si -y solo si- el nuevo tamaño del vector supera la capacidad del vector actual.

Entre las implementaciones de f1 y f2, la última es un poco más rápida aunque ambas usan back_inserter. El f2 es probablemente más rápido ya que lee el archivo en trozos, lo que permite que se produzca cierto almacenamiento en búfer.

2
Dusteh 14 dic. 2016 a las 15:46

Si es más pequeño que algunos GB, puede leer todos a la vez:

#include "sys/stat.h"
        ....

char* buf;
FILE* fin;
filename="myfile.cgt";
#ifdef WIN32
   struct stat st;
  if (stat(filename, &st) == -1) return 0;
#else
    struct _stat st;
if (_stat(filename, &st) == -1) return 0;
#endif
    fin = fopen(filename, "rb");
    if (!fin) return 0;
    buf = (char*)malloc(st.st_size);
    if (!buf) {fclose(fin); return 0;}
    fread(buf, st.st_size, 1, fin);
    fclose(fin);

No es necesario decir que debería usar "nuevo" en C ++ no malloc ()

-1
jurhas 14 dic. 2016 a las 14:28