Optymalizowanie czasu tworzenia się plików tekstowych

0

Zrobiłem taki prosty testowy kod, który tworzy 10 tysiący pustych plików tekstowych w podfolderze:

#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("\nCzas generowania plikow: %.2fs\n", (double)(clock() - tStart1)/CLOCKS_PER_SEC);
    std::cin.get();
    return 0;
}

Wszystkie 10 tyś. pustych plików stworzą się (na moim komputerze) w jakieś 3.5 sekundy.
Pytanie 1: pomijając te konwersje inta do stringa itp., czy jest coś co mógłbym już na tym etapie zoptymalizować aby było szybciej? Chodzi mi o użycie std::ofstream outfile - być może użycie czegoś innego jest znacznie szybsze w wykonywaniu się?

W każdym razie, 3.5 sekundy jest całkiem zadowalające względem tego:

Dodałem do funkcji jakiś prosty prototyp, który wypełnia owe pliki .txt: kilka razy zmienną i i jakimś stałym tekstem:

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);

        // tutaj mam to wypełnianie plików
        outfile << i << " stała " << i << " którą " << i << " wypełniam " << i << " plik " << i << " //coś// " << i
        << " tam " << i;
        i++;
    }
}

I teraz całość (tworzenie plików + ich wypełnianie) wykonuje się w... ~37 sekundy, więc jest już kolosalna różnica. A to tylko (lub aż?) 10 tysięcy plików.
Pytanie 2: czy da się tutaj coś zoptymalizować? Może jakaś szybsza alternatywa wypełniania plików, albo może zapomniałem o czymś oczywistym co spowalnia cały proces wykonywania się?

A może trochę wyolbrzymiam i taki czas jest całkiem normalny? Jest tu pole do optymalizowania?

3

Raczej patrzyłbym tutaj w stronę systemu operacyjnego/systemu plików/hardware na którym te pliki są niż na mikrooptymalizacje przy stringach.

1

Po co ci te 10 tysięcy plików?

0

Jak wyżej wspomniane sztuczki z systemem. Ale skoro są to jakieś niezależne pliki to możesz zawsze spróbować zrobić to wielowątkowo. Tyle że nie koniecznie może przynieść to jakieś efekty
ze stacka: klik

2

A może trochę wyolbrzymiam i taki czas jest całkiem normalny? Jest tu pole do optymalizowania?

Zdaje się, że problemem jest I/O po stronie systemu operacyjnego (dyski nie mają nieograniczonej szybkości koniec końców), więc niestety z punktu widzenia aplikacji niewiele możesz zdziałać.

Jeśli potem na tych plikach wykonujesz jeszcze jakieś dodatkowe operacje, mógłbyś je zapisywać do /dev/shm, wykonywać te magiczne operacje i dopiero potem zrzucać na dysk - dzięki temu przenosisz najwolniejszą operację na sam koniec; przy czym to jest tylko poniekąd obejście "problemu".

Równolegle do tematu: masz HDD czy SSD?

0

Na SSD całość wykonuje się w jakieś 35~37 sekund, na HDD w 30~32. Nie wiem jak to możliwe. Wątpię, abym miał uszkodzony dysk. Jedynie co, to ten SSD jest bardziej zasyfiony.

0

Sorry za double post, ale nadal nie rozumiem jak to działa.

Gdy tworzę 10k pustych plików, całość wykonuje się w 3 sekundy.

Gdy wypełniam te pliki tym:

outfile << i << " stała " << i << " którą " << i << " wypełniam " << i << " plik " << i << " //coś// " << i
        << " tam " << i;

całość wykonuje się w ~35 sekund.

Natomiast gdy tworzę tylko jeden plik .txt, i wypełniam ten jeden plik tym czym wypełniłbym wszystkie 10 000 razem wzięte:

while (i < 10000) {
        outfile << i << " some " << i << " constant " << i << " text " << i << " . . . "
        << i << " --more text-- " << i << " --even more-- " << i << std::endl;
        i++;
        }

W rezultacie powstaje jeden plik .txt który waży ~1Mb - całość wykonuje się w 0.09 sekundy.

Zatem:

  • tworzenie się plików jest szybkie
  • wypełnianie jednego plików sporą ilości danych jest bardzo szybkie
  • ale wypełnianie wielu plików małą ilością danych jest bardzo wolne

Dlaczego tak się dzieję, i czy mogę jakoś temu zapobiec?

Na marginesie: jest ktoś w stanie u siebie skompilować ten program, stworzyć podfolder "Results" w lokalizacji skompilowanego .exeka i sprawdzić w ile czasu się to wykona u Ciebie?

2

Za dużo wnioskujesz.

  • tworzenie pustych plików jest tanie, zapewne jest na to special case w kodzie FS
  • generowanie danych jest relatywnie szybkie (porównaj też jak to wygląda bez flushowania)
  • buforowany zapis jest relatywnie szybki

Pobaw się bezpośrednio z API systemowym (WinAPI/POSIX) i zobacz jakie tam uzyskujesz czasy. Zobacz jak wygląda to samo z komendą touch i zapisaniem danych za pomocą skryptu bashowego. Podepnij ramdisk i zobacz na nim.

1

Jeszcze mozna sprobowac to zrownoleglic, powinno pojsc calkiem sprawnie o ile nie opierasz sie o limity sprzetowego I/O.

Pare lat temu zrobilem proste porownanie odczytu liniowego oraz zapisu duzych plikow (rozmiar w GB). Zysk WriteFile/ReadFile vs iostream to bylo okolo 50%, dla mnie wtedy to bylo wystarczajace jednak dzisiejsze kompilatory oraz kilka generacji systemu Windows do przodu to zupelnie inny temat i nalezaloby przeprowadzic taki test ponownie.

3

Jak chcesz to naprawde szybko zrobic to jest mozliwe ze duzo szybciej bedzie to dzialac na customowym file systemie. Zwlaszcza case "zrob 1 mln pustych plikow" mozna pewnie zrobic ze 100x szybciej od jakiegokolwiek OSa. Ktos od gamedev albo embedded powinien Ci umiec pomoc.

Wazne zebys nie myslal o tym jak o uniwersalnym rozwiazaniu - bo jesli takie to ma byc to raczej sie nie uda. Trzeba wziac pod uwage tylko wymagania zlecone.

Szukaj pod haslem "custom file system" plus nazwa OS. Na Linux powinno byc latwiej niz na Windows.

Pytanie jaka wydajnosc uzyskujesz na linuksowym ramdysku?

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0