Bardzo szybkie pisanie pliku binarnego w C ++

241

Próbuję zapisać ogromne ilości danych na moim dysku SSD (dysk SSD). I przez ogromne kwoty mam na myśli 80 GB.

Przeglądałem sieć w poszukiwaniu rozwiązań, ale najlepsze, jakie wymyśliłem, to:

#include <fstream>
const unsigned long long size = 64ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
    std::fstream myfile;
    myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
    //Here would be some error handling
    for(int i = 0; i < 32; ++i){
        //Some calculations to fill a[]
        myfile.write((char*)&a,size*sizeof(unsigned long long));
    }
    myfile.close();
}

Skompilowany z Visual Studio 2010 i pełnymi optymalizacjami i działający pod Windows7 ten program osiąga maksimum około 20 MB / s. Naprawdę przeszkadza mi to, że Windows może kopiować pliki z innego dysku SSD na ten dysk SSD w zakresie od 150 MB / s do 200 MB / s. Co najmniej 7 razy szybciej. Dlatego uważam, że powinienem być w stanie jechać szybciej.

Jakieś pomysły, jak mogę przyspieszyć pisanie?

Dominic Hofer
źródło
11
Czy wyniki pomiaru czasu wykluczyły czas potrzebny na wykonanie obliczeń w celu wypełnienia []?
catchmeifyoutry
7
Właściwie to wcześniej to zrobiłem. Używając prostego fwrite(), mogłem uzyskać około 80% maksymalnej prędkości zapisu. Tylko dzięki FILE_FLAG_NO_BUFFERINGnie mogłem uzyskać maksymalnej prędkości.
Mysticial
10
Nie jestem pewien, czy sprawiedliwe jest porównywanie zapisywanych plików z kopiowaniem SSD na SSD. Może być tak, że SSD-SSD działa na niższym poziomie, unikając bibliotek C ++ lub używając bezpośredniego dostępu do pamięci (DMA). Kopiowanie czegoś nie jest tym samym, co zapisywanie dowolnych wartości do pliku o swobodnym dostępie.
Igor F.,
4
@IgorF .: To tylko błędne spekulacje; jest to całkowicie uczciwe porównanie (jeśli nic innego, na korzyść pisania plików). Kopiowanie na dysk w systemie Windows to tylko odczyt i zapis; nic szczególnego / skomplikowanego / innego nie dzieje się pod spodem.
user541686,
5
@MaximYegorushkin: Link lub tak się nie stało. : P
user541686,

Odpowiedzi:

233

Wykonało to zadanie (w 2012 r.):

#include <stdio.h>
const unsigned long long size = 8ULL*1024ULL*1024ULL;
unsigned long long a[size];

int main()
{
    FILE* pFile;
    pFile = fopen("file.binary", "wb");
    for (unsigned long long j = 0; j < 1024; ++j){
        //Some calculations to fill a[]
        fwrite(a, 1, size*sizeof(unsigned long long), pFile);
    }
    fclose(pFile);
    return 0;
}

Właśnie taktowałem 8 GB w 36 sekund, czyli około 220 MB / s, i myślę, że to maksymalnie zwiększa pojemność mojego dysku SSD. Warto również zauważyć, że kod w pytaniu używał jednego rdzenia 100%, podczas gdy ten kod używa tylko 2-5%.

Wielkie dzięki wszystkim.

Aktualizacja : 5 lat minęło już w 2017 roku. Kompilatory, sprzęt, biblioteki i moje wymagania uległy zmianie. Dlatego wprowadziłem pewne zmiany w kodzie i dokonałem kilku nowych pomiarów.

Najpierw kod:

#include <fstream>
#include <chrono>
#include <vector>
#include <cstdint>
#include <numeric>
#include <random>
#include <algorithm>
#include <iostream>
#include <cassert>

std::vector<uint64_t> GenerateData(std::size_t bytes)
{
    assert(bytes % sizeof(uint64_t) == 0);
    std::vector<uint64_t> data(bytes / sizeof(uint64_t));
    std::iota(data.begin(), data.end(), 0);
    std::shuffle(data.begin(), data.end(), std::mt19937{ std::random_device{}() });
    return data;
}

long long option_1(std::size_t bytes)
{
    std::vector<uint64_t> data = GenerateData(bytes);

    auto startTime = std::chrono::high_resolution_clock::now();
    auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
    myfile.write((char*)&data[0], bytes);
    myfile.close();
    auto endTime = std::chrono::high_resolution_clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}

long long option_2(std::size_t bytes)
{
    std::vector<uint64_t> data = GenerateData(bytes);

    auto startTime = std::chrono::high_resolution_clock::now();
    FILE* file = fopen("file.binary", "wb");
    fwrite(&data[0], 1, bytes, file);
    fclose(file);
    auto endTime = std::chrono::high_resolution_clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}

long long option_3(std::size_t bytes)
{
    std::vector<uint64_t> data = GenerateData(bytes);

    std::ios_base::sync_with_stdio(false);
    auto startTime = std::chrono::high_resolution_clock::now();
    auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
    myfile.write((char*)&data[0], bytes);
    myfile.close();
    auto endTime = std::chrono::high_resolution_clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}

int main()
{
    const std::size_t kB = 1024;
    const std::size_t MB = 1024 * kB;
    const std::size_t GB = 1024 * MB;

    for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option1, " << size / MB << "MB: " << option_1(size) << "ms" << std::endl;
    for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option2, " << size / MB << "MB: " << option_2(size) << "ms" << std::endl;
    for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option3, " << size / MB << "MB: " << option_3(size) << "ms" << std::endl;

    return 0;
}

Ten kod kompiluje się z Visual Studio 2017 i g ++ 7.2.0 (nowe wymagania). Uruchomiłem kod z dwoma ustawieniami:

  • Laptop, Core i7, SSD, Ubuntu 16.04, g ++ Wersja 7.2.0 z -std = c ++ 11 -march = natywny -O3
  • Desktop, Core i7, SSD, Windows 10, Visual Studio 2017 Version 15.3.1 with / Ox / Ob2 / Oi / Ot / GT / GL / Gy

Co dało następujące pomiary (po zrzuceniu wartości dla 1 MB, ponieważ były to oczywiste wartości odstające): Zarówno razy opcja 1, jak i opcja 3 maksymalizowały mój dysk SSD. Nie spodziewałem się tego, ponieważ opcja 2 była wtedy najszybszym kodem na mojej starej maszynie.wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

TL; DR : Moje pomiary wskazują użycie std::fstreamprzez FILE.

Dominic Hofer
źródło
8
+1 Tak, to była pierwsza rzecz, której spróbowałem. FILE*jest szybszy niż strumienie. Nie spodziewałbym się takiej różnicy, ponieważ i tak powinien był być związany z operacjami we / wy.
Mysticial
12
Czy możemy dojść do wniosku, że operacje we / wy w stylu C są (dziwnie) znacznie szybsze niż strumienie C ++?
SChepurin
21
@SChepurin: Jeśli jesteś pedantyczny, prawdopodobnie nie. Jeśli jesteś praktyczny, prawdopodobnie tak. :)
user541686,
10
Czy mógłbyś wyjaśnić (dla dunce jak C ++ jak ja) różnicę między tymi dwoma podejściami i dlaczego ten działa o wiele szybciej niż oryginał?
Mike Chamberlain,
11
Czy prepending ios::sync_with_stdio(false);robi jakąkolwiek różnicę w kodzie ze strumieniem? Jestem ciekawy, jak duża jest różnica między używaniem tej linii, a nie, ale nie mam wystarczająco szybkiego dysku, aby sprawdzić skrzynkę narożną. A jeśli jest jakaś prawdziwa różnica.
Artur Czajka
24

Spróbuj wykonać następujące czynności:

  • Mniejszy rozmiar bufora. Pisanie ~ 2 MiB na raz może być dobrym początkiem. Na moim ostatnim laptopie było ~ 512 KiB, ale nie testowałem jeszcze na moim dysku SSD.

    Uwaga: zauważyłem, że bardzo duże bufory mają tendencję do zmniejszania wydajności. Zauważyłem wcześniej straty prędkości przy użyciu buforów 16-MiB zamiast buforów 512-KiB.

  • Użyj _open(lub _topenjeśli chcesz mieć poprawny system Windows), aby otworzyć plik, a następnie użyj _write. Będzie to prawdopodobnie uniknąć wielu buforowania, ale nie jest to pewne.

  • Korzystanie z funkcji specyficznych dla systemu Windows, takich jak CreateFilei WriteFile. Pozwoli to uniknąć buforowania w standardowej bibliotece.

użytkownik541686
źródło
Sprawdź wyniki testu porównawczego opublikowane online. Potrzebujesz albo zapisu 4kB z głębokością kolejki wynoszącą 32 lub więcej, albo zapisu 512 KB lub wyższego, aby uzyskać jakąkolwiek przyzwoitą przepustowość.
Ben Voigt,
@BenVoigt: Tak, to koreluje ze mną, mówiąc, że 512 KiB było dla mnie najlepszym miejscem. :)
user541686,
Tak. Z mojego doświadczenia wynika, że ​​mniejsze rozmiary buforów są zwykle optymalne. Wyjątkiem jest sytuacja, gdy używasz FILE_FLAG_NO_BUFFERING- w której większe bufory wydają się być lepsze. Ponieważ myślę, że FILE_FLAG_NO_BUFFERINGto właściwie DMA.
Mysticial
22

Nie widzę różnicy między std :: stream / FILE / device. Między buforowaniem a brakiem buforowania.

Uwaga:

  • Dyski SSD „mają tendencję” do zwalniania (niższe prędkości przesyłu) w miarę zapełniania się.
  • Dyski SSD „mają tendencję” do spowolnienia (niższe prędkości transferu) w miarę starzenia się (z powodu niedziałających bitów).

Widzę, że kod działa w 63 sekundy.
Zatem szybkość transferu: 260 M / s (mój dysk SSD wygląda nieco szybciej niż twój).

64 * 1024 * 1024 * 8 /*sizeof(unsigned long long) */ * 32 /*Chunks*/

= 16G
= 16G/63 = 260M/s

Nie otrzymuję wzrostu, przechodząc do FILE * ze std :: fstream.

#include <stdio.h>

using namespace std;

int main()
{
    
    FILE* stream = fopen("binary", "w");

    for(int loop=0;loop < 32;++loop)
    {
         fwrite(a, sizeof(unsigned long long), size, stream);
    }
    fclose(stream);

}

Tak więc strumień C ++ działa tak szybko, jak na to pozwala podstawowa biblioteka.

Ale myślę, że niesprawiedliwe jest porównywanie systemu operacyjnego z aplikacją zbudowaną na systemie operacyjnym. Aplikacja nie może przyjmować żadnych założeń (nie wie, że dyski są dyskami SSD), dlatego do przesyłania używa mechanizmów plików systemu operacyjnego.

Chociaż system operacyjny nie musi przyjmować żadnych założeń. Potrafi określić typy zaangażowanych napędów i zastosować optymalną technikę przesyłania danych. W tym przypadku bezpośredni transfer z pamięci do pamięci. Spróbuj napisać program, który kopiuje 80G z jednej lokalizacji w pamięci do innej i sprawdź, jak szybko to jest.

Edytować

Zmieniłem kod, aby korzystać z wywołań niższego poziomu:
tj. Bez buforowania.

#include <fcntl.h>
#include <unistd.h>


const unsigned long long size = 64ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
    int data = open("test", O_WRONLY | O_CREAT, 0777);
    for(int loop = 0; loop < 32; ++loop)
    {   
        write(data, a, size * sizeof(unsigned long long));
    }   
    close(data);
}

Nie miało to żadnej różnicy.

UWAGA : Mój dysk jest dyskiem SSD, jeśli masz normalny dysk, możesz zauważyć różnicę między dwiema powyższymi technikami. Ale, jak się spodziewałem, buforowanie i buforowanie (przy zapisywaniu dużych fragmentów większych niż rozmiar bufora) nie ma znaczenia.

Edycja 2:

Czy wypróbowałeś najszybszą metodę kopiowania plików w C ++

int main()
{
    std::ifstream  input("input");
    std::ofstream  output("ouptut");

    output << input.rdbuf();
}
Martin York
źródło
5
Nie przegłosowałem, ale twój rozmiar bufora jest za mały. Zrobiłem to z tym samym 512 MB buforem, z którego korzysta OP i otrzymuję 20 MB / s ze strumieniami w porównaniu do 90 MB / s z FILE*.
Mysticial
Również twoja droga z fwrite (a, sizeof (niepodpisany długi długi), rozmiar, strumień); zamiast fwrite (a, 1, size * sizeof (niepodpisany długi długi), pFile); daje mi 220 MB / s przy porcjach 64 MB na zapis.
Dominic Hofer
2
@Mysticial: Zaskakuje mnie, że rozmiar bufora robi różnicę (choć ja ci wierzę). Bufor jest przydatny, gdy masz dużo małych zapisów, dzięki czemu urządzenie bazowe nie przeszkadza wielu żądaniom. Ale kiedy piszesz duże fragmenty, nie ma potrzeby buforowania podczas pisania / czytania (na urządzeniu blokującym). W związku z tym dane powinny być przekazywane bezpośrednio do urządzenia bazowego (omijając w ten sposób bufor). Chociaż jeśli widzisz różnicę, byłoby to sprzeczne z tym i zastanawiam się, dlaczego zapis w ogóle używa bufora.
Martin York,
2
Najlepszym rozwiązaniem NIE jest zwiększenie wielkości bufora, ale usunięcie bufora i spowodowanie, aby zapis przekazał dane bezpośrednio do urządzenia bazowego.
Martin York,
1
@Mysticial: 1) Nie ma małych kawałków => Zawsze jest wystarczająco duży (w tym przykładzie). W tym przypadku fragmenty mają 512 M 2) To jest dysk SSD (zarówno mój, jak i OP), więc nic z tego nie ma znaczenia. Zaktualizowałem swoją odpowiedź.
Martin York,
13

Najlepszym rozwiązaniem jest zaimplementowanie zapisu asynchronicznego z podwójnym buforowaniem.

Spójrz na linię czasu:

------------------------------------------------>
FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|

„F” oznacza czas wypełnienia bufora, a „W” oznacza czas zapisania bufora na dysk. Problem marnotrawstwa czasu między zapisywaniem buforów do pliku. Jednak wdrażając pisanie w osobnym wątku, możesz od razu zacząć wypełniać następny bufor:

------------------------------------------------> (main thread, fills buffers)
FF|ff______|FF______|ff______|________|
------------------------------------------------> (writer thread)
  |WWWWWWWW|wwwwwwww|WWWWWWWW|wwwwwwww|

F - wypełnienie 1. bufora
f - wypełnienie 2. bufora
W - zapisanie 1. bufora do pliku
w - zapisanie 2. bufora do pliku
_ - poczekaj na zakończenie operacji

To podejście z zamianą buforów jest bardzo przydatne, gdy wypełnienie bufora wymaga bardziej złożonych obliczeń (stąd więcej czasu). Zawsze implementuję klasę CSequentialStreamWriter, która ukrywa w sobie zapis asynchroniczny, więc dla użytkownika końcowego interfejs ma tylko funkcje zapisu.

Rozmiar bufora musi być wielokrotnością rozmiaru klastra dysku. W przeciwnym razie skończysz na niskiej wydajności, pisząc jeden bufor do 2 sąsiednich klastrów dyskowych.

Zapis ostatniego bufora.
Kiedy wywołujesz funkcję Write po raz ostatni, musisz upewnić się, że bieżący bufor jest zapełniony, również powinien zostać zapisany na dysk. Zatem CSequentialStreamWriter powinien mieć osobną metodę, powiedzmy Finalize (końcowe opróżnianie bufora), która powinna zapisywać na dysku ostatnią porcję danych.

Obsługa błędów.
Podczas gdy kod zaczyna zapełniać drugi bufor, a pierwszy jest zapisywany w osobnym wątku, ale z jakiegoś powodu zapis kończy się niepowodzeniem, główny wątek powinien być świadomy tego niepowodzenia.

------------------------------------------------> (main thread, fills buffers)
FF|fX|
------------------------------------------------> (writer thread)
__|X|

Załóżmy, że interfejs CSequentialStreamWriter ma funkcję Write, która zwraca wartość bool lub zgłasza wyjątek, a więc mając błąd w oddzielnym wątku, musisz zapamiętać ten stan, więc przy następnym wywołaniu Write lub Finilize w głównym wątku metoda zwróci Fałsz lub spowoduje wyjątek. I tak naprawdę nie ma znaczenia, w którym momencie przestałeś zapełniać bufor, nawet jeśli po awarii zapisałeś jakieś dane - najprawdopodobniej plik byłby uszkodzony i bezużyteczny.

HandMadeOX
źródło
3
Wykonywanie operacji we / wy równolegle z obliczeniami jest bardzo dobrym pomysłem, ale w systemie Windows nie należy używać do tego wątków. Zamiast tego użyj „Overlapped I / O”, która nie blokuje jednego z twoich wątków podczas wywołania I / O. Oznacza to, że prawie nie musisz się martwić synchronizacją wątków (po prostu nie uzyskuj dostępu do bufora, który używa aktywnej operacji I / O).
Ben Voigt
11

Proponuję spróbować mapowania plików . Używałem mmapw przeszłości w środowisku UNIX i byłem pod wrażeniem wysokiej wydajności, jaką mogłem osiągnąć

Ralph
źródło
1
@nalply To wciąż działające, wydajne i interesujące rozwiązanie, o którym należy pamiętać.
Yam Marcovic
stackoverflow.com/a/2895799/220060 o zaletach i wadach mmapa. Szczególnie uwaga: „W przypadku czystego sekwencyjnego dostępu do pliku, nie zawsze jest to również lepsze rozwiązanie [...]” Również stackoverflow.com/questions/726471 , skutecznie mówi, że w systemie 32-bitowym jesteś ograniczony do 2 lub 3 GB. - tak przy okazji, to nie ja głosowałem tę odpowiedź.
dokładnie
8

Czy możesz FILE*zamiast tego użyć i zmierzyć osiągnięte wyniki? fwrite/writeZamiast fstream: należy użyć kilku opcji :

#include <stdio.h>

int main ()
{
  FILE * pFile;
  char buffer[] = { 'x' , 'y' , 'z' };
  pFile = fopen ( "myfile.bin" , "w+b" );
  fwrite (buffer , 1 , sizeof(buffer) , pFile );
  fclose (pFile);
  return 0;
}

Jeśli zdecydujesz się użyć write, spróbuj czegoś podobnego:

#include <unistd.h>
#include <fcntl.h>

int main(void)
{
    int filedesc = open("testfile.txt", O_WRONLY | O_APPEND);

    if (filedesc < 0) {
        return -1;
    }

    if (write(filedesc, "This will be output to testfile.txt\n", 36) != 36) {
        write(2, "There was an error writing to testfile.txt\n", 43);
        return -1;
    }

    return 0;
}

Radziłbym również zajrzeć memory map. To może być twoja odpowiedź. Kiedyś musiałem przetworzyć plik 20 GB w innym, aby zapisać go w bazie danych, a plik nawet się nie otwiera. Tak więc rozwiązaniem jest wykorzystanie mapy pamięci. Zrobiłem to Pythonjednak.

cybertextron
źródło
W rzeczywistości bezpośredni FILE*odpowiednik oryginalnego kodu używającego tego samego bufora 512 MB uzyskuje pełną prędkość. Twój obecny bufor jest za mały.
Mysticial
1
@Mysticial Ale to tylko przykład.
cybertextron,
W większości systemów 2odpowiada standardowemu błędowi, ale nadal zaleca się stosowanie STDERR_FILENOzamiast niego 2. Inną ważną kwestią jest to, że jednym z możliwych błędów, których nie można uzyskać, jest EINTR, ponieważ gdy otrzymasz sygnał przerwania, nie jest to prawdziwy błąd i powinieneś po prostu spróbować ponownie.
Peyman
6

Spróbuj użyć wywołań API open () / write () / close () i eksperymentuj z wielkością bufora wyjściowego. Mam na myśli: nie przepuszczaj całego bufora „wiele-wiele-bajtów” naraz, wykonaj kilka zapisów (tj. TotalNumBytes / OutBufferSize). OutBufferSize może mieć od 4096 bajtów do megabajta.

Kolejna próba - użyj WinAPI OpenFile / CreateFile i skorzystaj z tego artykułu MSDN, aby wyłączyć buforowanie (FILE_FLAG_NO_BUFFERING). I ten artykuł MSDN na temat WriteFile () pokazuje, jak uzyskać rozmiar bloku, aby dysk mógł poznać optymalny rozmiar bufora.

W każdym razie std :: ofstream jest opakowaniem i może występować blokowanie operacji we / wy. Pamiętaj, że przejście całej macierzy N-gigabajtów również zajmuje trochę czasu. Kiedy piszesz mały bufor, dostaje się do pamięci podręcznej i działa szybciej.

Viktor Latypov
źródło
6

fstreamsame w sobie nie są wolniejsze niż strumienie C, ale zużywają więcej procesora (szczególnie jeśli buforowanie nie jest odpowiednio skonfigurowane). Gdy procesor się nasyca, ogranicza szybkość operacji we / wy.

Przynajmniej implementacja MSVC 2015 kopiuje 1 znak na raz do bufora wyjściowego, gdy bufor strumienia nie jest ustawiony (patrz streambuf::xsputn). Upewnij się więc, że ustawiłeś bufor strumienia (> 0) .

Za fstreampomocą tego kodu mogę uzyskać prędkość zapisu 1500 MB / s (pełna prędkość mojego dysku M.2 SSD) :

#include <iostream>
#include <fstream>
#include <chrono>
#include <memory>
#include <stdio.h>
#ifdef __linux__
#include <unistd.h>
#endif
using namespace std;
using namespace std::chrono;
const size_t sz = 512 * 1024 * 1024;
const int numiter = 20;
const size_t bufsize = 1024 * 1024;
int main(int argc, char**argv)
{
  unique_ptr<char[]> data(new char[sz]);
  unique_ptr<char[]> buf(new char[bufsize]);
  for (size_t p = 0; p < sz; p += 16) {
    memcpy(&data[p], "BINARY.DATA.....", 16);
  }
  unlink("file.binary");
  int64_t total = 0;
  if (argc < 2 || strcmp(argv[1], "fopen") != 0) {
    cout << "fstream mode\n";
    ofstream myfile("file.binary", ios::out | ios::binary);
    if (!myfile) {
      cerr << "open failed\n"; return 1;
    }
    myfile.rdbuf()->pubsetbuf(buf.get(), bufsize); // IMPORTANT
    for (int i = 0; i < numiter; ++i) {
      auto tm1 = high_resolution_clock::now();
      myfile.write(data.get(), sz);
      if (!myfile)
        cerr << "write failed\n";
      auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count());
      cout << tm << " ms\n";
      total += tm;
    }
    myfile.close();
  }
  else {
    cout << "fopen mode\n";
    FILE* pFile = fopen("file.binary", "wb");
    if (!pFile) {
      cerr << "open failed\n"; return 1;
    }
    setvbuf(pFile, buf.get(), _IOFBF, bufsize); // NOT important
    auto tm1 = high_resolution_clock::now();
    for (int i = 0; i < numiter; ++i) {
      auto tm1 = high_resolution_clock::now();
      if (fwrite(data.get(), sz, 1, pFile) != 1)
        cerr << "write failed\n";
      auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count());
      cout << tm << " ms\n";
      total += tm;
    }
    fclose(pFile);
    auto tm2 = high_resolution_clock::now();
  }
  cout << "Total: " << total << " ms, " << (sz*numiter * 1000 / (1024.0 * 1024 * total)) << " MB/s\n";
}

Wypróbowałem ten kod na innych platformach (Ubuntu, FreeBSD) i zauważyłem brak różnic we / wy, ale różnicę wykorzystania procesora wynoszącą około 8: 1 ( fstreamużywane 8 razy więcej procesora ). Można więc sobie wyobrazić, że gdybym miał szybszy dysk, fstreamzapis byłby wolniejszy niż stdiowersja.

rustyx
źródło
3

Spróbuj użyć plików zmapowanych w pamięci.

qehgt
źródło
@ Mehrdad, ale dlaczego? Ponieważ jest to rozwiązanie zależne od platformy?
qehgt
3
Nie ... to dlatego, że aby wykonać szybkie sekwencyjne zapisywanie plików, musisz zapisać duże ilości danych naraz. (Powiedzmy, że fragmenty 2-MiB są prawdopodobnie dobrym punktem wyjścia.) Pliki odwzorowane w pamięci nie pozwalają kontrolować ziarnistości, więc jesteś na łasce tego, co menedżer pamięci zdecyduje się pobrać / buforować dla ciebie. Ogólnie rzecz biorąc, nigdy nie widziałem, aby były one tak skuteczne jak normalne czytanie / pisanie ReadFilei takie dla dostępu sekwencyjnego, chociaż dla dostępu losowego mogą być lepsze.
user541686,
Ale pliki mapowane w pamięci są używane przez system operacyjny na przykład do stronicowania. Myślę, że jest to wysoce zoptymalizowany (pod względem prędkości) sposób odczytu / zapisu danych.
qehgt
7
@Mysticial: Ludzie „wiedzą” wiele rzeczy, które są po prostu złe.
Ben Voigt
1
@qehgt: W razie potrzeby stronicowanie jest znacznie bardziej zoptymalizowane pod kątem dostępu losowego niż dostępu sekwencyjnego. Odczyt 1 strony danych jest znacznie wolniejszy niż odczyt 1 megabajta danych w jednej operacji.
user541686,
3

Jeśli skopiujesz coś z dysku A na dysk B w Eksploratorze, Windows zastosuje DMA. Oznacza to, że w przypadku większości procesów kopiowania procesor zasadniczo nie robi nic innego, jak powiedzieć kontrolerowi dysku, gdzie umieścić i uzyskać dane, eliminując cały krok w łańcuchu i taki, który wcale nie jest zoptymalizowany do przenoszenia dużych ilości danych - i mam na myśli sprzęt.

To, co robisz, wiąże się bardzo z procesorem. Chcę wskazać Ci część „Niektóre obliczenia dotyczące wypełnienia []”. Co uważam za niezbędne. Generujesz [], następnie kopiujesz z [] do bufora wyjściowego (to właśnie robi fstream :: write), a następnie generujesz ponownie itp.

Co robić? Wielowątkowość! (Mam nadzieję, że masz procesor wielordzeniowy)

  • widelec.
  • Użyj jednego wątku, aby wygenerować dane []
  • Użyj drugiej, aby zapisać dane z [] na dysk
  • Będziesz potrzebował dwóch tablic a1 [] i a2 [] i przełączać się między nimi
  • Będziesz potrzebować synchronizacji między wątkami (semafory, kolejka komunikatów itp.)
  • Używaj funkcji niższego poziomu, niebuforowanych, takich jak funkcja WriteFile wspomniana przez Mehrdada
podwójny
źródło
1

Jeśli chcesz szybko pisać do strumieni plików, możesz zwiększyć strumień bufora odczytu:

wfstream f;
const size_t nBufferSize = 16184;
wchar_t buffer[nBufferSize];
f.rdbuf()->pubsetbuf(buffer, nBufferSize);

Ponadto, podczas zapisywania dużej ilości danych do plików, czasem logicznie jest zwiększyć rozmiar pliku zamiast fizycznie, dzieje się tak, ponieważ podczas logicznego rozszerzania pliku system plików nie zeruje nowego miejsca przed zapisaniem do niego. Rozsądnie jest też logicznie rozszerzyć plik bardziej, niż jest to potrzebne, aby zapobiec dużej ilości plików. Plik logiczny rozbudowa jest obsługiwane w systemie Windows poprzez wywołanie SetFileValidDatalub xfsctlz XFS_IOC_RESVSP64systemów XFS.


źródło
0

kompiluję mój program w gcc w GNU / Linux i mingw w Win 7 i Win XP i działałem dobrze

możesz użyć mojego programu i aby utworzyć plik o wielkości 80 GB, po prostu zmień wiersz 33 na

makeFile("Text.txt",1024,8192000);

po wyjściu z programu plik zostanie zniszczony, a następnie sprawdź plik, gdy jest uruchomiony

mieć program, który chcesz, po prostu zmień program

pierwszy to program Windows, a drugi to GNU / Linux

http://mustafajf.persiangig.com/Projects/File/WinFile.cpp

http://mustafajf.persiangig.com/Projects/File/File.cpp

Ethaan
źródło