C ++ 'da çok hızlı bir ikili dosya yazma


241

SSD (katı hal sürücüsü) üzerine büyük miktarda veri yazmaya çalışıyorum. Ve büyük miktarlarda 80GB demek istiyorum.

Çözümler için web'e göz attım, ancak en iyi buldum:

#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();
}

Visual Studio 2010 ve tam optimizasyonlarla derlenmiş ve Windows7 altında çalıştırılan bu program yaklaşık 20MB / sn. Beni gerçekten rahatsız eden şey, Windows'un başka bir SSD'den bu SSD'ye 150MB / s ile 200MB / s arasında bir yerde dosya kopyalayabilmesidir. Yani en az 7 kat daha hızlı. Bu yüzden daha hızlı gidebilmeliyim diye düşünüyorum.

Yazılarımı nasıl hızlandırabileceğim hakkında bir fikrin var mı?


11
Zamanlama sonuçlarınız, hesaplarınızı [] doldurmak için gereken süreyi hariç tuttu mu?
catchmeifyoutry

7
Aslında bu görevi daha önce yapmıştım. Basit kullanarak fwrite()en yüksek yazma hızlarının yaklaşık% 80'ini alabilirim. Sadece ile FILE_FLAG_NO_BUFFERINGmaksimum hız elde edebildim.
Mysticial

10
Dosya yazmanızı SSD-SSD kopyalamayla karşılaştırmanın adil olmadığından emin değilim. SSD-SSD, C ++ kitaplıklarından kaçınarak veya doğrudan bellek erişimi (DMA) kullanarak daha düşük bir seviyede çalışabilir. Bir şeyi kopyalamak, rastgele erişim dosyasına rastgele değerler yazmakla aynı şey değildir.
Igor

4
@IgorF .: Bu sadece yanlış bir spekülasyon; mükemmel bir karşılaştırma (başka bir şey değilse, dosya yazma lehine). Windows'ta bir sürücüyü kopyalamak sadece okumak ve yazmaktır; altında hiçbir şey fantezi / karmaşık / farklı oluyor.
user541686

5
@MaximYegorushkin: Bağlantı ya da olmadı. : P
user541686

Yanıtlar:


233

Bu işi yaptı (2012 yılında):

#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;
}

Az önce 36GB'de 8GB'yi zamanladım, bu da yaklaşık 220MB / s ve SSD'mi en üst düzeye çıkarıyor. Ayrıca, söz konusu kod% 100 bir çekirdek kullanırken, bu kod sadece% 2-5 kullanır.

Herkese çok teşekkürler.

Güncelleme : 5 yıl geçti şimdi 2017. Derleyiciler, donanım, kütüphaneler ve gereksinimlerim değişti. Bu yüzden kodda bazı değişiklikler yaptım ve bazı yeni ölçümler yaptım.

İlk önce kodu:

#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;
}

Bu kod, Visual Studio 2017 ve g ++ 7.2.0 (yeni gereksinimler) ile derlenir. Kodu iki kurulumla çalıştırdım:

  • Dizüstü bilgisayar, Core i7, SSD, Ubuntu 16.04, g ++ Sürüm 7.2.0 -std = c ++ 11 ile-mart = yerli -O3
  • Masaüstü, Core i7, SSD, Windows 10, Visual Studio 2017 Sürüm 15.3.1 / Ox / Ob2 / Oi / Ot / GT / GL / Gy ile

Bu, aşağıdaki ölçümleri verdi (değerleri 1MB için hendeledikten sonra, bariz aykırı olduklarından): Her iki kez option1 ve option3 SSD'mi maks. Bunu görmeyi beklemiyordum, çünkü option2 eskiden eski makinemdeki en hızlı koddu.resim açıklamasını buraya girin resim açıklamasını buraya girin

TL; DR : Ölçümlerim std::fstreamaşırı kullanıldığını gösteriyor FILE.


8
+1 Evet, ilk denediğim şey buydu. FILE*akışlardan daha hızlıdır. Zaten I / O bağlı olması gerektiği için böyle bir fark beklemezdim.
Mysticial

12
C tarzı G / Ç'nin (garip olarak) C ++ akışlarından çok daha hızlı olduğu sonucuna varabilir miyiz?
SChepurin

21
@SChepurin: Bilgiçlik yapıyorsanız, muhtemelen değil. Eğer pratik olursanız, muhtemelen evet. :)
user541686

10
Lütfen (benim gibi bir C ++ dunce için) iki yaklaşım arasındaki farkı açıklayabilir misiniz ve bu neden orijinalinden çok daha hızlı çalışıyor?
Mike Chamberlain

11
Önceki ios::sync_with_stdio(false);kod, akışlı kod için herhangi bir fark yaratıyor mu ? Sadece bu çizgiyi kullanmak arasında değil, ne kadar büyük bir fark olduğunu merak ediyorum, ama köşe kasasını kontrol etmek için yeterince hızlı diskim yok. Ve eğer gerçek bir fark varsa.
Artur Czajka

24

Aşağıdakileri sırayla deneyin:

  • Daha küçük arabellek boyutu. Bir seferde ~ 2 MiB yazmak iyi bir başlangıç ​​olabilir. Son dizüstü bilgisayarımda ~ 512 KiB en güzel noktaydı, ancak SSD'mi henüz test etmedim.

    Not: Çok büyük arabelleklerin performansı düşürme . Daha önce 512-KiB tamponlar yerine 16-MiB tamponları kullanarak hız kayıplarını fark ettim.

  • Kullanım _open(veya _topenWindows-doğru olmasını istiyorsanız), ardından dosyayı açmak kullanmak _write. Bu muhtemelen çok fazla tamponlamayı önleyecektir, ancak kesin değildir.

  • CreateFileVe gibi Windows'a özgü işlevleri kullanma WriteFile. Bu, standart kitaplıkta herhangi bir arabelleğe almayı önleyecektir.


Çevrimiçi olarak yayınlanan karşılaştırmalı değerlendirme sonuçlarını kontrol edin. Herhangi bir iyi verim elde etmek için kuyruk derinliği 32 veya daha fazla olan 4kB yazmalara veya 512K veya daha yüksek yazmalara ihtiyacınız vardır.
Ben Voigt

@BenVoigt: Evet, 512 KiB'nin benim için tatlı bir yer olduğunu söyleyerek benimle bağlantılıdır. :)
user541686

Evet. Deneyimlerime göre, daha küçük tampon boyutları genellikle en uygunudur. İstisna, FILE_FLAG_NO_BUFFERINGdaha büyük arabelleklerin daha iyi olma eğiliminde olduğu kullanımdır. Bence FILE_FLAG_NO_BUFFERINGhemen hemen DMA.
Mysticial

22

Std :: stream / FILE / device arasında bir fark görmüyorum. Tamponlama ile tamponlama arasında.

Ayrıca dikkat:

  • SSD sürücüler dolduruldukça yavaşlamaya (düşük aktarım hızları) eğilimlidir.
  • SSD sürücüler yaşlandıkça (çalışmayan bitler nedeniyle) yavaşlamaya (düşük aktarım hızları) "eğilimlidir".

Kod 63 saniyede çalıştırmak görüyorum.
Böylece aktarım hızı: 260M / s ( SSD'm sizinkinden biraz daha hızlı görünüyor).

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

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

Std :: fstream dosyasından FILE * klasörüne geçerek artış görmüyorum.

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

}

Böylece C ++ akışı temel kütüphanenin izin verdiği kadar hızlı çalışıyor.

Ancak işletim sistemini işletim sistemi üzerine kurulu bir uygulama ile karşılaştırmanın adil olmadığını düşünüyorum. Uygulama hiçbir varsayımda bulunamaz (sürücülerin SSD olduğunu bilmez) ve bu nedenle aktarım için işletim sisteminin dosya mekanizmalarını kullanır.

İşletim sisteminin herhangi bir varsayım yapmasına gerek yoktur. İlgili sürücülerin türlerini söyleyebilir ve verileri aktarmak için en uygun tekniği kullanabilir. Bu durumda, doğrudan belleğe bellek aktarımı. 80G'yi bellekteki 1 konumdan diğerine kopyalayan bir program yazmayı deneyin ve bunun ne kadar hızlı olduğunu görün.

Düzenle

Daha düşük seviye çağrıları kullanmak için kodumu değiştirdim:
yani arabelleğe alma.

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

Bu fark yaratmadı.

NOT : Sürücüm normal bir sürücünüz varsa bir SSD sürücüsüdür, yukarıdaki iki teknik arasında bir fark görebilirsiniz. Ancak beklediğim gibi arabelleğe alma ve arabelleğe alma (arabellek boyutundan daha büyük büyük parçalar yazarken) hiçbir fark yaratmaz.

Düzenleme 2:

C ++ 'da dosya kopyalamanın en hızlı yöntemini denediniz mi?

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

    output << input.rdbuf();
}

5
İndirmedim, ancak arabellek boyutunuz çok küçük. OP'nin kullandığı 512 MB arabellekle yaptım ve akışlarla 20 MB / sn, 90 MB / sn ile alıyorum FILE*.
Mysticial

Ayrıca fwrite (a, sizeof (unsigned long long), size, stream) ile yolunuz; fwrite (a, 1, size * sizeof (unsigned long long), pFile) yerine; Bana yazma başına 64 MB'lik yığınlarla 220MB / s veriyor.
Dominic Hofer

2
@Mysticial: Tampon boyutunun bir fark yarattığını şaşırtıyor (sana inandığım halde). Buffer, altta yatan cihazın birçok istekle uğraşmaması için çok sayıda küçük yazınız olduğunda kullanışlıdır. Ancak büyük parçalar yazarken, yazma / okuma sırasında bir engellemeye gerek yoktur (bir engelleme cihazında). Bu nedenle veriler doğrudan alttaki cihaza iletilmelidir (bu nedenle tamponu atlayarak). Bir fark görürseniz, bu bununla çelişir ve yazmanın neden bir tampon kullandığını merak etmem gerekir.
Martin York

2
En iyi çözüm, arabellek boyutunu artırmak DEĞİLDİR , ancak arabelleği çıkarmak ve yazma işlemini yapmak için verileri doğrudan temel cihaza geçirmektir.
Martin York

1
@Mysticial: 1) Küçük parçalar yok => Her zaman yeterince büyüktür (bu örnekte). Bu durumda parçalar 512M 2'dir) Bu bir SSD sürücüdür (hem benim hem de OP), bu yüzden bunların hiçbiri ilgili değildir. Cevabımı güncelledim.
Martin York

13

En iyi çözüm, çift arabelleğe alma ile bir zaman uyumsuz yazı uygulamaktır.

Zaman çizgisine bakın:

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

'F' tampon doldurma süresini ve 'W' tamponu diske yazma süresini temsil eder. Yani arabellekleri dosyaya yazmak arasında zaman kaybı sorunu. Bununla birlikte, ayrı bir iş parçacığına yazma uygulayarak, bir sonraki arabelleği hemen doldurmaya başlayabilirsiniz:

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

F - 1. tampon doldurma
f - 2. tampon doldurma
W - dosyaya 1. tampon
yazma w - dosyaya 2. tampon yazma
_ - işlem tamamlandığında bekleyin

Tampon swapları ile bu yaklaşım, bir tamponun doldurulması daha karmaşık hesaplama gerektirdiğinde (dolayısıyla daha fazla zaman) çok yararlıdır. Her zaman içinde zaman uyumsuz yazma gizler bir CSequentialStreamWriter sınıfı uygulamak, böylece son kullanıcı için arabirim sadece yazma işlevleri vardır.

Ve arabellek boyutu disk kümesi boyutunun katı olmalıdır. Aksi takdirde, bitişik 2 disk kümesine tek bir arabellek yazarak düşük performans elde edersiniz.

Son tamponun yazılması.
Yazma işlevini son kez çağırdığınızda, geçerli arabelleğin doldurulmakta olduğundan emin olmanız gerekir. Bu nedenle, CSequentialStreamWriter'ın, verilerin son kısmını diske yazması gereken Finalize (son tampon yıkaması) diyelim ayrı bir yöntemi olmalıdır.

Hata yönetimi.
Kod 2. arabelleği doldurmaya başlarken ve birincisi ayrı bir iş parçacığına yazılır, ancak yazma bir nedenle başarısız olursa, ana iş parçacığı bu hatanın farkında olmalıdır.

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

Bir CSequentialStreamWriter arabiriminin Yazma işlevinin bool döndürdüğünü veya bir istisna attığını varsayalım, bu nedenle ayrı bir iş parçacığında bir hata olması durumunda, bu durumu hatırlamanız gerekir, bu nedenle ana iş parçacığında bir dahaki sefere Yazma veya Sonlandırmayı çağırdığınızda, yöntem geri döner Yanlış ya da bir istisna atacak. Ve hatadan sonra bazı veriler yazmış olsanız bile, hangi noktada bir tampon doldurmayı bıraktığınız önemli değildir - büyük olasılıkla dosya bozuk ve işe yaramaz olacaktır.


3
G / Ç işlemlerini hesaplamalarla paralel yapmak çok iyi bir fikirdir, ancak Windows'da bunu gerçekleştirmek için iş parçacıkları kullanmamalısınız. Bunun yerine, G / Ç çağrısı sırasında iş parçacıklarınızdan birini engellemeyen "Çakışan G / Ç" kullanın. Bu, iplik senkronizasyonu hakkında endişelenmeniz gerekmediği anlamına gelir (sadece etkin bir G / Ç işlemi olan bir arabelleğe erişmeyin).
Ben Voigt

11

Dosya eşlemeyi denemenizi öneririm . Ben kullanılan mmapUNIX ortamında, geçmişte ve ben elde edebiliriz yüksek performans etkilendim


1
@nalply Hala akılda tutmak için çalışan, verimli ve ilginç bir çözüm.
Yam Marcovic

mmap artıları hakkında eksileri hakkında stackoverflow.com/a/2895799/220060 . Özellikle not "Dosyaya saf sıralı erişim için, aynı zamanda her zaman daha iyi bir çözüm değildir [...]" Ayrıca stackoverflow.com/questions/726471 , 32-bit bir sistemde 2 veya 3 GB. - bu arada, bu cevabı reddeden ben değilim.
nalply

8

FILE*Bunun yerine kullanabilir ve elde ettiğiniz performansı ölçebilir misiniz? Bunun fwrite/writeyerine birkaç seçenek kullanmaktır fstream:

#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;
}

Kullanmaya karar verirseniz write, benzer bir şey deneyin:

#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;
}

Ayrıca bakmanızı tavsiye ederim memory map. Bu senin cevabın olabilir. Bir keresinde veritabanında saklamak için başka bir 20GB dosyasını işlemek zorunda kaldım ve dosyayı açmıyor bile. Bu nedenle, moemory haritasını kullanmanın çözümü. Bunu ben de yaptım Python.


Aslında, FILE*aynı 512 MB arabelleği kullanan orijinal kodun düz bir eşdeğeri tam hız alır. Geçerli arabelleğiniz çok küçük.
Mysticial

1
@Mysticial Ama bu sadece bir örnek.
cybertextron

Çoğu sistemde 2standart hataya karşılık gelir, ancak bunun STDERR_FILENOyerine kullanmanız önerilir 2. Başka bir önemli sorun, bir kesinti sinyali aldığınızda alabileceğiniz olası bir hatanın EINTR olmasıdır, bu gerçek bir hata değildir ve tekrar denemeniz gerekir.
Peyman

6

Open () / write () / close () API çağrılarını kullanmayı deneyin ve çıktı arabelleği boyutunu deneyin. Demek istediğim bütün "çok-çok baytlık" arabelleği bir kerede geçmeyin, birkaç yazma yapın (yani TotalNumBytes / OutBufferSize). OutBufferSize, 4096 bayt ile megabayt arasında olabilir.

Başka bir deneyin WinAPI OpenFile / CreateFile kullanın ve arabelleğe almayı (FILE_FLAG_NO_BUFFERING) kapatmak için bu MSDN makalesini kullanın. Ve WriteFile () üzerindeki bu MSDN makalesi optimum tampon boyutunu bilmek sürücü için blok boyutunu nasıl gösterir.

Her neyse, std :: ofstream bir sarıcıdır ve G / Ç işlemlerinde engelleme olabilir. Tüm N-gigabayt dizisinin geçişinin de biraz zaman aldığını unutmayın. Küçük bir arabellek yazarken önbelleğe ulaşır ve daha hızlı çalışır.


6

fstreams, C akışlarından daha yavaş değildir, ancak daha fazla CPU kullanırlar (özellikle arabelleğe alma doğru yapılandırılmamışsa). CPU doygunlaştığında, G / Ç hızını sınırlar.

En azından bir akış arabelleği ayarlanmadığında MSVC 2015 uygulaması bir kerede 1 karakter çıktı arabelleğine kopyalar (bkz. streambuf::xsputn). Bu nedenle bir akış arabelleği (> 0) ayarladığınızdan emin olun .

fstreamBu kodu kullanarak 1500MB / s (M.2 SSD'm tam hızı) yazma hızı elde edebilirim :

#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";
}

Diğer platformlarda bu kodu (Ubuntu FreeBSD) güvenilir ve bir I / O oranı farklılıkları fark ancak bir CPU kullanımı 1 (: 8 ila yaklaşık farkı fstreamkullanılan 8 kat daha fazla CPU ). Daha hızlı bir disk olsaydım, fstreamyazma işlemi stdiosürümden daha kısa sürede yavaşlardı .


3

Bellek eşlemeli dosyaları kullanmaya çalışın.


@Mehrdad ama neden? Çünkü bu platforma bağımlı bir çözüm mü?
qehgt

3
Hayır ... çünkü hızlı ardışık dosya yazımı yapmak için, aynı anda büyük miktarda veri yazmanız gerekir. (Diyelim ki, 2-MiB parçaları muhtemelen iyi bir başlangıç ​​noktasıdır.) Bellek eşlemeli dosyalar, ayrıntı düzeyini kontrol etmenize izin vermez, bu nedenle bellek yöneticisinin sizin için önceden getirmeye / arabelleğe almaya karar verdiği her şeyin merhametindesiniz. Genel olarak, sıradan ReadFileerişim için normal okuma / yazma ve bu kadar etkili olmalarını hiç görmedim , ancak rastgele erişim için daha iyi olabilirler.
user541686

Ancak bellek eşlemeli dosyalar örneğin çağrı için OS tarafından kullanılır. Veri okumak / yazmak için oldukça optimize edilmiş (hız açısından) bir yol olduğunu düşünüyorum.
qehgt

7
@Mysticial: İnsanlar basitçe yanlış olan bir çok şeyi bilirler.
Ben Voigt

1
@ qehgt: Herhangi bir şey varsa, sayfalama rasgele erişim için sıralı erişime göre çok daha optimize edilir. 1 sayfa veri okumak, tek bir işlemde 1 megabayt veri okumaktan çok daha yavaştır .
user541686

3

Explorer'da A diskinden B diskine bir şey kopyalarsanız, Windows DMA kullanır. Bu, kopyalama işleminin çoğu için, CPU'nun temel olarak disk denetleyicisine nereye koyacağını ve veri alacağını, zincirdeki tüm bir adımı ortadan kaldıracağını ve büyük miktarlarda hareket etmek için hiç optimize edilmediğini söylemekten başka bir şey yapmayacağı anlamına gelir. ve donanım demek istiyorum.

ne sen yapmak CPU bir sürü içerir. Sizi "[] [[]] bölümünü doldurmak için bazı hesaplamaları göstermek istiyorum. Bence bu çok önemli. Bir [] oluşturursunuz, daha sonra bir [] öğesinden bir çıktı arabelleğine kopyalarsınız (fstream :: write'ın yaptığı şey budur), sonra tekrar üretirsiniz, vb.

Ne yapalım? Çoklu kullanım! (Umarım çok çekirdekli işlemciniz vardır)

  • çatal.
  • [] Veri oluşturmak için bir iş parçacığı kullanın
  • [] 'Den diske veri yazmak için diğerini kullanın
  • İki a1 [] ve a2 [] dizisine ihtiyacınız olacak ve aralarında geçiş yapacaksınız
  • İleti dizileriniz arasında bir çeşit senkronizasyona ihtiyacınız olacak (semaforlar, mesaj kuyruğu vb.)
  • Mehrdad tarafından belirtilen WriteFile işlevi gibi alt düzey, arabelleksiz işlevleri kullanın

1

Dosya akışlarına hızlı yazmak istiyorsanız, okuma arabelleğini daha büyük bir akış haline getirebilirsiniz:

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

Ayrıca, dosyalara çok fazla veri yazarken , fiziksel olarak dosya boyutunu mantıksal olarak genişletmek bazen daha hızlıdır , çünkü bir dosyayı mantıksal olarak genişletirken dosya sisteminin yazmadan önce yeni alanı sıfırlamamasıdır. Ayrıca, mantıksal olarak birçok dosya uzantısını önlemek için gerekenden daha fazla mantıksal olarak genişletmek akıllıdır. Mantıksal dosya uzantısı arayarak Windows üzerinde desteklenir SetFileValidDataya xfsctlile XFS_IOC_RESVSP64XFS sistemlerde.


0

im GNU / Linux gcc programımı derlemek ve kazanmak 7 ve xp kazanmak mingw ve iyi çalıştı

programımı kullanabilir ve 80 GB'lık bir dosya oluşturmak için 33 numaralı satırı

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

programdan çıkıldığında dosya imha edilecek ve daha sonra dosyayı kontrol edin

sadece istediğiniz programı değiştirmek için

firt biri windows programı, ikincisi GNU / Linux içindir

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

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.