Ответ 1
Вот что я собрал до сих пор:
Буферизация
Если по умолчанию буфер очень мал, увеличение размера буфера может определенно повысить производительность:
- он уменьшает количество обращений к жесткому диску.
- он уменьшает количество системных вызовов
Буфер можно установить, обратившись к базовой реализации streambuf
.
char Buffer[N];
std::ifstream file("file.txt");
file.rdbuf()->pubsetbuf(Buffer, N);
// the pointer reader by rdbuf is guaranteed
// to be non-null after successful constructor
Предупреждение, предоставленное @iavr: в соответствии с cppreference лучше всего вызвать pubsetbuf
, прежде чем открывать файл. Различные реализации стандартной библиотеки в противном случае имеют разные типы поведения.
Локальная обработка:
Локаль может выполнять преобразование символов, фильтрацию и более умные трюки, в которых задействованы числа или даты. Они проходят сложную систему динамической диспетчеризации и виртуальных вызовов, поэтому их удаление может помочь обрезать штраф.
По умолчанию C
locale подразумевается не выполнять никакого преобразования, а также быть равномерным на всех машинах. Это хороший по умолчанию.
Синхронизация:
Я не видел улучшения производительности с помощью этого средства.
Можно получить глобальную настройку (статический член std::ios_base
) с помощью статической функции sync_with_stdio
.
Размеры:
Играя с этим, я играл с простой программой, скомпилированной с помощью gcc 3.4.2
на SUSE 10p3 с -O2
.
C: 7.76532e + 06
С++: 1.0874e + 07
Что представляет собой замедление около 20%
... для кода по умолчанию. Действительно, подделка буфером (на C или С++) или параметрами синхронизации (С++) не принесла никаких улучшений.
Результаты другими:
@Irfy на g++ 4.7.2-2ubuntu1, -O3, виртуализированный Ubuntu 11.10, 3.5.0-25-generic, x86_64, достаточно ram/cpu, 196MB нескольких "find/ → largefile.txt" работает
C: 634572 С++: 473222
С++ на 25% быстрее
@Matteo Italia на g++ 4.4.5, -O3, Ubuntu Linux 10.10 x86_64 со случайным файлом размером 180 МБ
C: 910390
С++: 776016
С++ на 17% быстрее
@Bogatyr на g++ i686-apple-darwin10-g++ - 4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664), Mac mini, 4 ГБ оперативной памяти, бездействия, кроме этого теста с файлом данных 168 МБ
C: 4.34151e + 06
С++: 9.14476e + 06
С++ 111% медленнее
@Asu on clang++ 3.8.0-2ubuntu4, Kubuntu 16.04 Linux 4.8-rc3, 8GB RAM, i5 Haswell, Crucial SSD, файл данных 88MB (архив tar.xz)
C: 270895 С++: 162799
С++ на 66% быстрее
Таким образом, ответ таков: это проблема с качеством реализации и действительно зависит от платформы:/
Полный код здесь для тех, кто интересуется бенчмаркингом:
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstdio>
#include <sys/time.h>
template <typename Func>
double benchmark(Func f, size_t iterations)
{
f();
timeval a, b;
gettimeofday(&a, 0);
for (; iterations --> 0;)
{
f();
}
gettimeofday(&b, 0);
return (b.tv_sec * (unsigned int)1e6 + b.tv_usec) -
(a.tv_sec * (unsigned int)1e6 + a.tv_usec);
}
struct CRead
{
CRead(char const* filename): _filename(filename) {}
void operator()() {
FILE* file = fopen(_filename, "r");
int count = 0;
while ( fscanf(file,"%s", _buffer) == 1 ) { ++count; }
fclose(file);
}
char const* _filename;
char _buffer[1024];
};
struct CppRead
{
CppRead(char const* filename): _filename(filename), _buffer() {}
enum { BufferSize = 16184 };
void operator()() {
std::ifstream file(_filename, std::ifstream::in);
// comment to remove extended buffer
file.rdbuf()->pubsetbuf(_buffer, BufferSize);
int count = 0;
std::string s;
while ( file >> s ) { ++count; }
}
char const* _filename;
char _buffer[BufferSize];
};
int main(int argc, char* argv[])
{
size_t iterations = 1;
if (argc > 1) { iterations = atoi(argv[1]); }
char const* oldLocale = setlocale(LC_ALL,"C");
if (strcmp(oldLocale, "C") != 0) {
std::cout << "Replaced old locale '" << oldLocale << "' by 'C'\n";
}
char const* filename = "largefile.txt";
CRead cread(filename);
CppRead cppread(filename);
// comment to use the default setting
bool oldSyncSetting = std::ios_base::sync_with_stdio(false);
double ctime = benchmark(cread, iterations);
double cpptime = benchmark(cppread, iterations);
// comment if oldSyncSetting declaration is commented
std::ios_base::sync_with_stdio(oldSyncSetting);
std::cout << "C : " << ctime << "\n"
"C++: " << cpptime << "\n";
return 0;
}