Оптимизация скорости создания файлов .txt

Я написал следующий простой тестовый код, который создает 10 000 пустых файлов .txt в подкаталоге.

#include <iostream>
#include <time.h>
#include <string>
#include <fstream>

void CreateFiles()
{
    int i = 1;
    while (i <= 10000) {
        int filename = i;
        std::string string_i = std::to_string(i);
        std::string file_dir = ".\\results\\"+string_i+".txt";
        std::ofstream outfile(file_dir);
        i++;
    }
}

int main()
{
    clock_t tStart1 = clock();
    CreateFiles();
    printf("\nHow long it took to make files: %.2fs\n", (double)(clock() - tStart1)/CLOCKS_PER_SEC);
    std::cin.get();
    return 0;
}

Все нормально работает. Все 10 000 файлов .txt создаются в течение ~3.55 секунд. (используя мой компьютер)

Вопрос 1: Игнорируя преобразование из int в std::string и т. д., Есть ли что-нибудь, что я мог бы здесь оптимизировать, чтобы программа могла быстрее создавать файлы? Я конкретно имею в виду использование std::ofstream outfile - возможно, использование чего-то другого было бы значительно быстрее?

Тем не менее, ~3,55 секунды удовлетворительны по сравнению со следующим:

Я изменил функцию так, чтобы прямо сейчас она также заполняла файлы .txt некоторыми случайными целочисленными данными i и некоторым постоянным текстом:

void CreateFiles()
{
    int i = 1;
    while (i <= 10000) {
        int filename = i;
        std::string string_i = std::to_string(i);
        std::string file_dir = ".\\results\\"+string_i+".txt";
        std::ofstream outfile(file_dir);

        // Here is the part where I am filling the .txt with some data
        outfile << i << " some " << i << " constant " << i << " text " << i << " . . . " 
        << i << " --more text-- " << i << " --even more-- " << i;
        i++;
    }
}

И теперь все (создание файлов .txt и заполнение их короткими данными) выполняется в течение ... ~37 секунд. Это огромная разница. И это всего 10 000 файлов.

Вопрос 2: Можно что-нибудь здесь оптимизировать? Возможно, существует какая-то альтернатива, которая быстрее заполняла бы файлы .txt. Или, может быть, я забыл о чем-то очень очевидном, что замедляет весь процесс?

Или, может быть, я немного преувеличиваю и секунды ~37 кажутся нормальными и оптимизированными?

Спасибо, что поделились своими мыслями!

Ничего нового, потоки io не очень быстрые в C++. Вам следует попробовать аналог C, если ваша единственная цель - производительность. Также посмотрите {fmt}, AFAIK он может работать с потоками напрямую.

Asu 26.12.2018 22:41

Да, запись в файлы стоит дорого, особенно. когда вам нужна высокая пропускная способность. Не удивительно разница на порядок при записи чего-либо в файлы.

TriskalJM 26.12.2018 22:44

Также можно попробовать просто вывести outfile << 'a'? Если вы получите сумасшедшие более высокие числа, чем ничего не написав, я подозреваю, что виновата производительность файловой системы.

Asu 26.12.2018 22:46

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

Galik 26.12.2018 22:49
Есть ли что-нибудь, что я мог бы здесь оптимизировать, чтобы программа могла быстрее создавать файлы? - Будьте осторожны, пытаясь оптимизировать любой код, связанный с записью файлов. То, что может быть оптимальным для вашего оборудования, может не иметь никакого эффекта или, что еще хуже, замедлить работу при работе на другом оборудовании.
PaulMcKenzie 26.12.2018 22:50

@Asu outfile << 'a'; создает файлы 10000 .txt за секунды 4.70.

weno 26.12.2018 22:52

Попробуйте записать через неформатированные функции ввода, такие как 'std :: ostream :: write`, и измерить их производительность (убедитесь, что вы записали такое же количество байтов). Если это значительно быстрее, ваша виноватая - форматирование, если то же самое - платформа.

SergeyA 26.12.2018 22:53
ostream::write дал еще худшие результаты D: + - 10%
weno 26.12.2018 23:15

Попробуйте записать на RAMdrive, чтобы увидеть, не ограничивает ли вас производительность механического диска.

Mark Setchell 26.12.2018 23:30

Какую систему используете? Если вы используете MSVC++, вы используете реализацию, которая пыталась доказать, что потоки IOStream работают медленно. Конечно, это на самом деле не доказывало то, что намеревалось доказать, а скорее то, что одну реализацию можно сделать медленной. Для этой конкретной реализации использование std::ios_base::sync_with_stdio(false); в качестве первого элемента в main() оказало довольно большое влияние на производительность, когда я попробовал это (что, однако, было ~ 20 лет назад).

Dietmar Kühl 26.12.2018 23:49

Связанный? stackoverflow.com/questions/12997131/…

Paul Sanders 26.12.2018 23:52

Наличие тысяч файлов в одном каталоге приводит к медленному поиску и там.

Davis Herring 26.12.2018 23:55

Win10 x64, Code :: Blocks, G ++ с включенным флагом C++ 14.

weno 27.12.2018 00:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
13
193
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Скорость создания файла зависит от оборудования, чем быстрее диск, тем быстрее вы можете создавать файлы.

Это очевидно из того факта, что Я запустил ваш код на процессоре ARM (Snapdragon 636, на мобильном телефоне, использующем termux), теперь мобильные телефоны имеют флэш-память, которая очень быстра, когда дело доходит до ввода-вывода. Итак, большую часть времени он работал менее 3 секунд, а иногда и 5 секунд. Это изменение ожидается, поскольку накопитель должен обрабатывать многопроцессорные операции чтения и записи. Вы сообщили, что для вашего оборудования потребовалось 47 секунд.Отсюда можно смело заключить, что скорость ввода-вывода в значительной степени зависит от оборудования.


Тем не менее я подумал о некоторой оптимизации вашего кода и использовал 2 разных подхода.

  • Использование аналога C для ввода-вывода

  • Использование C++, но запись фрагментом за один раз.

Я запустил симуляцию на своем телефоне. Я запускал его 50 раз, и вот результаты.

  • C был самым быстрым, занимая в среднем 2,73928 секунды, чтобы написать ваше слово в 10000 текстовых файлах, используя fprintf.

  • Написание на C++ всей строки за один раз заняло 2,7899 секунды. Я использовал sprintf, чтобы поместить полную строку в char [], а затем написал, используя оператор << в ofstream.

  • C++ Normal (ваш код) занял 2,8752 секунды

Это ожидаемое поведение, запись кусками выполняется быстрее. Прочтите ответ это, почему. Без сомнения, C был самым быстрым.

Вы можете заметить здесь, что разница не столь значительна, но если вы используете оборудование с медленным вводом-выводом, это становится значительным.


Вот код, который я использовал для моделирования. Вы можете проверить это самостоятельно, но не забудьте заменить аргумент std::system своими собственными командами (разные для Windows).

#include <iostream>
#include <time.h>
#include <string>
#include <fstream>
#include <stdio.h>

void CreateFiles()
{
    int i = 1;
    while (i <= 10000) {
       // int filename = i;
        std::string string_i = std::to_string(i);
        std::string file_dir = "./results/"+string_i+".txt";
        std::ofstream outfile(file_dir);

        // Here is the part where I am filling the .txt with some data
        outfile << i << " some " << i << " constant " << i << " text " << i << " . . . " 
        << i << " --more text-- " << i << " --even more-- " << i;
        i++;
    }
}

void CreateFilesOneGo(){
    int i = 1;
    while(i<=10000){
        std::string string_i = std::to_string(i);
        std::string file_dir = "./results3/" + string_i + ".txt";
        char buffer[256];
        sprintf(buffer,"%d some %d constant %d text %d . . . %d --more text-- %d --even more-- %d",i,i,i,i,i,i,i);
        std::ofstream outfile(file_dir);
        outfile << buffer;
        i++;
    }
}
        
void CreateFilesFast(){
    int i = 1;
    while(i<=10000){
    // int filename = i;
    std::string string_i = std::to_string(i);
    std::string file_dir = "./results2/"+string_i+".txt";
    FILE *f = fopen(file_dir.c_str(), "w");
    fprintf(f,"%d some %d constant %d text %d . . . %d --more text-- %d --even more-- %d",i,i,i,i,i,i,i);
    fclose(f);
    i++;
    }
}

int main()
{
    double normal = 0, one_go = 0, c = 0;
    for (int u=0;u<50;u++){
        std::system("mkdir results results2 results3");
        
        clock_t tStart1 = clock();
        CreateFiles();
        //printf("\nNormal : How long it took to make files: %.2fs\n", (double)(clock() - tStart1)/CLOCKS_PER_SEC);
        normal+=(double)(clock() - tStart1)/CLOCKS_PER_SEC;
       
        tStart1 = clock();
        CreateFilesFast();
        //printf("\nIn C : How long it took to make files: %.2fs\n", (double)(clock() - tStart1)/CLOCKS_PER_SEC);
        c+=(double)(clock() - tStart1)/CLOCKS_PER_SEC;
        
        tStart1 = clock();
        CreateFilesOneGo();
        //printf("\nOne Go : How long it took to make files: %.2fs\n", (double)(clock() - tStart1)/CLOCKS_PER_SEC);
        one_go+=(double)(clock() - tStart1)/CLOCKS_PER_SEC;
        
        std::system("rm -rf results results2 results3");
        std::cout<<"Completed "<<u+1<<"\n";
    }
    
    std::cout<<"C on average took : "<<c/50<<"\n";
    std::cout<<"Normal on average took : "<<normal/50<<"\n";
    std::cout<<"One Go C++ took : "<<one_go/50<<"\n";
    
    return 0;
}

Также в качестве компилятора я использовал clang-7.0.

Если у вас есть другой подход, дайте мне знать, я его тоже протестирую. Если вы обнаружите ошибку, дайте мне знать, я исправлю ее как можно скорее.

Обратите внимание, что это также зависит от компилятора, Linux / gcc предоставляет по умолчанию IO_BUFSIZ (я считаю, что в настоящее время переименован в LIO_BUFSIZE) байтов 8192, в то время как Windows VS предоставляет только 512. Таким образом, «увеличение» до фиксированного 4096, как указано в связанном ответе, фактически сократит размер буфера ввода-вывода по умолчанию в Linux вдвое. (молодцы по тестированию и отчетности)

David C. Rankin 27.12.2018 08:56

Другие вопросы по теме