बहुत तेजी से C ++ में एक बाइनरी फ़ाइल लिखना


241

मैं अपने SSD (सॉलिड स्टेट ड्राइव) पर भारी मात्रा में डेटा लिखने की कोशिश कर रहा हूं। और भारी मात्रा में मेरा मतलब है 80GB।

मैंने समाधान के लिए वेब को ब्राउज़ किया, लेकिन सबसे अच्छा मैं यह आया था:

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

विजुअल स्टूडियो 2010 और पूर्ण अनुकूलन के साथ संकलित किया गया है और विंडोज 7 के तहत चलाया जाता है। वास्तव में मुझे परेशान करने वाली बात यह है कि विंडोज 150MB / s और 200MB / s के बीच कहीं और इस SSD की अन्य SSD से फाइल कॉपी कर सकता है। तो कम से कम 7 गुना तेज। इसलिए मुझे लगता है कि मुझे तेजी से जाने में सक्षम होना चाहिए।

किसी भी विचार मैं अपने लेखन को कैसे गति दे सकता हूं?


11
क्या आपके समय के परिणामों ने एक [] को भरने के लिए अपनी गणना करने में लगने वाले समय को बाहर कर दिया?
Julmeifyoutry

7
मैंने वास्तव में यह कार्य पहले किया है। सरल का उपयोग करके fwrite()मैं लगभग 80% चोटी लिखने की गति प्राप्त कर सकता हूं। केवल FILE_FLAG_NO_BUFFERINGमैं ही कभी अधिकतम गति प्राप्त कर सका था।
रहस्यमयी जूल

10
मुझे यकीन नहीं है कि आपकी फ़ाइल को SSD-to-SSD कॉपी करने के लिए लिखना उचित है। यह अच्छी तरह से हो सकता है कि एसएसडी-टू-एसएसडी निचले स्तर पर काम करता है, सी ++ पुस्तकालयों से परहेज करता है, या प्रत्यक्ष मेमोरी एक्सेस (डीएमए) का उपयोग करता है। किसी चीज़ की नकल करना एक यादृच्छिक एक्सेस फ़ाइल के लिए मनमाना मान लिखने के समान नहीं है।
इगोर एफ।

4
@ आईजीआरएफ .: यह सिर्फ गलत अटकल है; यह पूरी तरह से उचित तुलना है (यदि फ़ाइल लेखन के पक्ष में और कुछ नहीं)। विंडोज में एक ड्राइव पर नकल करना सिर्फ पढ़ना-लिखना है; कुछ भी नहीं फैंसी / जटिल / अलग चल रहा है।
user541686

5
@MaximYegorushkin: लिंक या ऐसा नहीं हुआ। : पी
user541686

जवाबों:


233

यह काम किया (वर्ष 2012 में):

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

मैंने अभी 36sec में 8GB की टाइमिंग की है, जो कि लगभग 220MB / s है और मुझे लगता है कि मेरे SSD को अधिकतम कर देता है। यह भी ध्यान दें, प्रश्न में कोड ने एक कोर 100% का उपयोग किया, जबकि यह कोड केवल 2-5% का उपयोग करता है।

सभी को बहुत-बहुत धन्यवाद।

अपडेट : 5 साल बीत चुके हैं अब 2017 है। कंपाइलर, हार्डवेयर, लाइब्रेरी और मेरी आवश्यकताएं बदल गई हैं। इसलिए मैंने कोड में कुछ बदलाव किए और कुछ नए माप किए।

पहले कोड:

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

यह कोड विजुअल स्टूडियो 2017 और जी ++ 7.2.0 (एक नई आवश्यकताओं) के साथ संकलित है। मैंने दो सेटअप के साथ कोड चलाया:

  • लैपटॉप, Core i7, SSD, Ubuntu 16.04, g ++ संस्करण 7.2.0 with -std = c ++ 11 -ार्च = मूल -O3
  • डेस्कटॉप, कोर i7, SSD, विंडोज 10, विज़ुअल स्टूडियो 2017 संस्करण 15.3.1 / ऑक्स / Ob2 / Oi / Ot / GT / GL / Gy के साथ

जिसने निम्नलिखित माप दिए (1 एमबी के लिए मानों को खोदने के बाद, क्योंकि वे स्पष्ट रूप से आउटलेयर थे): दोनों बार विकल्प 1 और विकल्प 3 मेरे एसएसडी को अधिकतम करते हैं। मुझे यह देखने की उम्मीद नहीं थी, क्योंकि विकल्प 2 तब मेरी पुरानी मशीन पर सबसे तेज़ कोड हुआ करता था।यहां छवि विवरण दर्ज करें यहां छवि विवरण दर्ज करें

टी एल; डॉ : मेरा माप का उपयोग करने से संकेत मिलता है std::fstreamअधिक FILE


8
+1 हाँ, यह पहली चीज़ थी जिसकी मैंने कोशिश की थी। FILE*धाराओं से तेज है। मैं इस तरह के अंतर की उम्मीद नहीं करता था क्योंकि इसे "I have" होना चाहिए, मैं वैसे भी बाध्य था।
रहस्यपूर्ण

12
क्या हम यह निष्कर्ष निकाल सकते हैं कि C- शैली I / O (+ अजीब) C ++ स्ट्रीम की तुलना में बहुत तेज है?
SChepurin

21
@ एससीपुरिन: यदि आप पांडित्यपूर्ण हैं, तो शायद नहीं। यदि आप व्यावहारिक हैं, तो शायद हाँ। :)
user541686

10
क्या आप कृपया बता सकते हैं (मेरे जैसे C ++ के लिए) दो दृष्टिकोणों के बीच का अंतर, और यह क्यों मूल की तुलना में बहुत तेजी से काम करता है?
माइक चैंबरलेन

11
क्या प्रीपेडिंग ios::sync_with_stdio(false);धारा के साथ कोड के लिए कोई अंतर करता है ? मैं बस उत्सुक हूं कि इस लाइन का उपयोग करने के बीच कितना बड़ा अंतर है और नहीं, लेकिन मेरे पास कोने के मामले की जांच करने के लिए पर्याप्त तेज डिस्क नहीं है। और अगर कोई वास्तविक अंतर है।
अर्तुर कज्जाका

24

क्रम में निम्नलिखित प्रयास करें:

  • छोटे बफर आकार। एक बार में ~ 2 MiB लिखना एक अच्छी शुरुआत हो सकती है। मेरे आखिरी लैपटॉप पर, ~ 512 KiB स्वीट स्पॉट था, लेकिन मैंने अभी तक अपने SSD पर परीक्षण नहीं किया है।

    नोट: मैंने देखा है कि बहुत बड़े बफ़र प्रदर्शन को कम करते हैं । मैंने पहले 512-KiB बफ़र्स के बजाय 16-MiB बफ़र्स का उपयोग करने के साथ स्पीड लॉस देखा है।

  • उपयोग _open(या _topenआप Windows-सही होने के लिए चाहते हैं) फ़ाइल खोलने के लिए है, तो का उपयोग _write। यह शायद बहुत से बफरिंग से बच जाएगा , लेकिन यह निश्चित नहीं है।

  • जैसे विंडोज-विशिष्ट कार्यों का उपयोग करना CreateFileऔर WriteFile। यह मानक पुस्तकालय में किसी भी बफरिंग से बचना होगा।


ऑनलाइन पोस्ट किए गए किसी भी बेंचमार्क परिणाम की जाँच करें। आपको या तो 4kB की जरूरत है 32 या अधिक की कतार की गहराई के साथ, या 512K या उच्चतर लिखता है, किसी भी प्रकार के सभ्य विवाद को पाने के लिए।
बेन वोइग्ट

@BenVoigt: हाँ, मेरे साथ यह कहते हुए कि 512 KiB मेरे लिए सबसे प्यारी जगह थी। :)
user541686

हाँ। मेरे अनुभव से, छोटे बफर आकार आमतौर पर इष्टतम होते हैं। अपवाद तब होता है जब आप उपयोग कर रहे होते हैं FILE_FLAG_NO_BUFFERING- जिसमें बड़े बफ़र्स बेहतर होते हैं। चूंकि मुझे लगता FILE_FLAG_NO_BUFFERINGहै कि बहुत ज्यादा डीएमए है।
रहस्यपूर्ण

22

मुझे std :: स्ट्रीम / FILE / डिवाइस में कोई अंतर नहीं दिखता है। बफरिंग और नॉन बफरिंग के बीच।

यह भी नोट करें:

  • एसएसडी ड्राइव "टेंड" को धीमा कर देता है (कम अंतरण दर) जैसा कि वे भरते हैं।
  • SSD ड्राइव को कम (कम ट्रांसफर रेट) धीमा कर देता है क्योंकि वे पुराने हो जाते हैं (काम न करने की वजह से)।

मैं 63 सेकंड में कोड रन देख रहा हूं।
इस प्रकार की एक स्थानांतरण दर: 260M / s (मेरी SSD आपकी तुलना में थोड़ी तेज दिखती है)।

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

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

मुझे std :: fstream से FILE * में जाने से कोई वृद्धि नहीं होती है।

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

}

इसलिए C ++ स्ट्रीम उतनी ही तेजी से काम कर रही है जितनी अंतर्निहित लाइब्रेरी अनुमति देगी।

लेकिन मुझे लगता है कि यह ओएस को एक ऐसे एप्लिकेशन से तुलना करना अनुचित है जो ओएस के शीर्ष पर बनाया गया है। एप्लिकेशन कोई अनुमान नहीं लगा सकता (यह नहीं जानता कि ड्राइव एसएसडी हैं) और इस प्रकार स्थानांतरण के लिए ओएस के फ़ाइल तंत्र का उपयोग करता है।

जबकि OS को कोई धारणा बनाने की आवश्यकता नहीं है। यह शामिल ड्राइव के प्रकारों को बता सकता है और डेटा को स्थानांतरित करने के लिए इष्टतम तकनीक का उपयोग कर सकता है। इस मामले में मेमोरी ट्रांसफर के लिए एक सीधा मेमोरी। एक प्रोग्राम लिखने की कोशिश करें जो 80G को मेमोरी में 1 स्थान से दूसरे स्थान पर कॉपी करता है और देखें कि यह कितना तेज़ है।

संपादित करें

मैंने निचले स्तर की कॉल का उपयोग करने के लिए अपना कोड बदल दिया:
अर्थात कोई बफरिंग नहीं।

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

इसने कोई भिन्नता नहीं बनाई।

नोट : मेरी ड्राइव एक एसएसडी ड्राइव है यदि आपके पास एक सामान्य ड्राइव है तो आप ऊपर दी गई दो तकनीकों के बीच अंतर देख सकते हैं। लेकिन जैसा कि मैंने गैर बफरिंग और बफ़रिंग की उम्मीद की थी (जब बफर साइज़ से अधिक बड़ी चोंच को छोड़ना) कोई फर्क नहीं पड़ता।

2 संपादित करें:

क्या आपने C ++ में फ़ाइलों को कॉपी करने का सबसे तेज़ तरीका आज़माया है

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

    output << input.rdbuf();
}

5
मैं नीचे नहीं गया, लेकिन आपका बफर आकार बहुत छोटा है। मैंने वही 512 एमबी बफर के साथ किया था जो ओपी उपयोग कर रहा है और मुझे 20 एमबी / एस स्ट्रीम के साथ 90 एमबी / एस के साथ मिलता है FILE*
रहस्यमयी जूल

इसके अलावा अपने तरीके से fwrite (a, sizeof (अहस्ताक्षरित लंबा लंबा), आकार, धारा); फ़ॉरराइट (a, 1, size * sizeof (अहस्ताक्षरित लंबा लंबा), pFile) के बजाय; मुझे लिखता है 64MB प्रति लेखन के साथ 220MB / s।
डोमिनिक हॉफर

2
@ मिस्टिक: यह मेरे आश्चर्य की बात है कि बफर आकार में फर्क पड़ता है (हालांकि मैं आपको मानता हूं)। जब आप बहुत छोटे लिखते हैं तो बफर उपयोगी होता है ताकि अंतर्निहित डिवाइस कई अनुरोधों से परेशान न हो। लेकिन जब आप बहुत बड़ा हिस्सा लिख ​​रहे होते हैं तो लिखने / पढ़ने (एक अवरुद्ध डिवाइस पर) को बफर की कोई आवश्यकता नहीं होती है। इस तरह के डेटा को अंतर्निहित डिवाइस पर सीधे पारित किया जाना चाहिए (इस प्रकार बफर से गुजरना)। यद्यपि यदि आपको एक अंतर दिखाई देता है तो यह विरोधाभास होगा और मेरा आश्चर्य होगा कि लेखन वास्तव में एक बफर का उपयोग क्यों कर रहा है।
मार्टिन

2
सबसे अच्छा समाधान नहीं है बफर आकार को बढ़ाने के लिए है, बल्कि बफर को हटाने के लिए और अंतर्निहित डिवाइस पर सीधे डेटा पास करने के लिए लिखें।
मार्टिन

1
@ मूल: 1) कोई छोटा हिस्सा नहीं हैं => यह हमेशा काफी बड़ा होता है (इस उदाहरण में)। इस मामले में विराम 512M 2 हैं) यह एक SSD ड्राइव (मेरा और OP दोनों) है, इसलिए इनमें से कोई भी प्रासंगिक नहीं है। मैंने अपना उत्तर अपडेट कर दिया है।
मार्टिन यॉर्क

13

सबसे अच्छा समाधान डबल बफ़रिंग के साथ एक async लेखन को लागू करना है।

समय रेखा देखें:

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

बफर भरने के लिए 'एफ' समय का प्रतिनिधित्व करता है, और 'डब्ल्यू' बफर को डिस्क पर लिखने के लिए समय का प्रतिनिधित्व करता है। तो फाइल करने के लिए बफ़र्स लिखने के बीच समय बर्बाद करने में समस्या। हालाँकि, एक अलग थ्रेड पर लेखन को लागू करके, आप अगले बफर को इस तरह तुरंत भरना शुरू कर सकते हैं:

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

एफ - 1 बफर
एफ भरना - 2 बफर
डब्ल्यू भरना - डब्ल्यू को फाइल करने के लिए 1 बफर
लिखना - फाइल करने के लिए 2 बफर लिखना
_ - संचालन पूरा होने तक प्रतीक्षा करें

बफर स्वैप के साथ यह दृष्टिकोण बहुत उपयोगी है जब बफर को भरने के लिए अधिक जटिल संगणना की आवश्यकता होती है (इसलिए, अधिक समय)। मैं हमेशा एक CSequentialStreamWriter वर्ग को लागू करता हूं जो अतुल्यकालिक लेखन को अंदर छिपाता है, इसलिए अंत-उपयोगकर्ता के लिए इंटरफ़ेस में केवल फ़ंक्शन लिखें।

और बफ़र आकार डिस्क क्लस्टर आकार का एक से अधिक होना चाहिए। अन्यथा, आप 2 निकटवर्ती डिस्क क्लस्टर में एक एकल बफर लिखकर खराब प्रदर्शन को समाप्त करेंगे।

आखिरी बफर लिखना।
जब आप अंतिम बार लिखें फ़ंक्शन को कॉल करते हैं, तो आपको यह सुनिश्चित करना होगा कि वर्तमान बफर भरा जा रहा है और साथ ही डिस्क पर भी लिखा जाना चाहिए। इस प्रकार CSequentialStreamWriter का एक अलग तरीका होना चाहिए, मान लें कि अंतिम रूप दें (अंतिम बफर फ्लश), जिसे डेटा के अंतिम भाग को डिस्क पर लिखना चाहिए।

गलती संभालना।
जबकि कोड 2 बफ़र भरना शुरू कर देता है, और 1 एक अलग थ्रेड पर लिखा जा रहा है, लेकिन किसी कारण से विफल रहता है, मुख्य थ्रेड को उस विफलता के बारे में पता होना चाहिए।

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

मान लें कि CSequentialStreamWriter के इंटरफ़ेस में फ़ंक्शन रिटर्न बूल है या एक अपवाद फेंकता है, इस प्रकार एक अलग थ्रेड पर कोई त्रुटि होती है, आपको उस स्थिति को याद रखना होगा, इसलिए अगली बार जब आप मुख्य थ्रेड पर लिखें या समाप्त करें, तो विधि वापस आ जाएगी। गलत या एक अपवाद फेंक देंगे। और यह वास्तव में मायने नहीं रखता है कि आपने किस बिंदु पर एक बफर भरना बंद कर दिया है, भले ही आपने विफलता के बाद कुछ डेटा आगे लिखा हो - सबसे अधिक संभावना है कि फ़ाइल दूषित और बेकार होगी।


3
परिकलन के साथ I / O का प्रदर्शन करना एक बहुत अच्छा विचार है, लेकिन विंडोज पर आपको इसे पूरा करने के लिए थ्रेड्स का उपयोग नहीं करना चाहिए। इसके बजाय, "ओवरलेप्ड आई / ओ" का उपयोग करें, जो आई / ओ कॉल के दौरान आपके किसी एक धागे को ब्लॉक नहीं करता है। इसका मतलब है कि आपको मुश्किल से थ्रेड सिंक्रोनाइज़ेशन के बारे में चिंता करना होगा (बस एक बफर का उपयोग न करें जिसके पास सक्रिय I / O ऑपरेशन है)।
बेन वोइगट

11

मेरा सुझाव है कि फ़ाइल मैपिंग का प्रयास करें । मैंने mmapयूनिक्स के माहौल में अतीत में इस्तेमाल किया था, और मैं उच्च प्रदर्शन से प्रभावित था जिसे मैं प्राप्त कर सकता था


1
@nalply यह अभी भी ध्यान में रखने के लिए एक कारगर, कुशल और दिलचस्प समाधान है।
यम मारकोविच

mmover की विपक्ष के बारे में stackoverflow.com/a/2895799/220060 । विशेष रूप से ध्यान दें "फ़ाइल के लिए शुद्ध अनुक्रमिक पहुंच के लिए, यह हमेशा बेहतर समाधान नहीं है [...]" इसके अलावा stackoverflow.com/questions/726471 , यह प्रभावी रूप से कहता है कि 32-बिट सिस्टम पर आप 2 या सीमित हैं 3 जीबी। - वैसे, यह मुझे नहीं है जिसने उस उत्तर को अस्वीकार कर दिया।
nalply

8

क्या आप FILE*इसके बजाय उपयोग कर सकते हैं , और आपके द्वारा प्राप्त प्रदर्शन को माप सकते हैं? विकल्पों में से एक के fwrite/writeबजाय का उपयोग करने के लिए है 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;
}

यदि आप उपयोग करने का निर्णय लेते हैं write, तो कुछ इसी तरह का प्रयास करें:

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

मैं आपको सलाह दूंगा कि आप इस पर गौर करें memory map। वह आपका उत्तर हो सकता है। एक बार मुझे डेटाबेस में स्टोर करने के लिए 20 जीबी की फाइल को प्रोसेस करना पड़ा, और फाइल भी नहीं खुल रही थी। तो समाधान के रूप में moemory मानचित्र का उपयोग करने के लिए। मैंने Pythonहालांकि इसमें किया था ।


दरअसल, एक FILE*ही 512 एमबी बफर का उपयोग करने वाले मूल कोड के एक सीधे-आगे समकक्ष को पूर्ण गति मिलती है। आपका वर्तमान बफर बहुत छोटा है।
15

1
@ मिस्टिक लेकिन यह सिर्फ एक उदाहरण है।
साइबरटैक्सट्रॉन

अधिकांश प्रणालियों में, 2मानक त्रुटि से मेल खाती है लेकिन यह अभी भी अनुशंसित है कि आप STDERR_FILENOइसके बजाय उपयोग करेंगे 2। एक और महत्वपूर्ण मुद्दा यह है कि आपके द्वारा प्राप्त किया जा सकने वाला एक संभावित इरोइन है जब आप एक बाधा संकेत प्राप्त करते हैं, तो यह एक वास्तविक त्रुटि नहीं है और आपको बस फिर से प्रयास करना चाहिए।
पैयमैन

6

ओपन () / राइट () / क्लोज़ () एपीआई कॉल का उपयोग करें और आउटपुट बफर आकार के साथ प्रयोग करें। मेरा मतलब है कि एक बार में पूरे "कई-कई-बाइट्स" बफ़र को पास न करें, कुछ लिखें (यानी, TotalNumBytes / OutBufferSize)। OutBufferSize 4096 बाइट्स से मेगाबाइट तक हो सकता है।

एक और कोशिश - WinAPI OpenFile / CreateFile का उपयोग करें और बफ़रिंग को बंद करने के लिए इस MSDN लेख का उपयोग करें (FILE_FLAG_NO_BUFFERING)। और WriteFile पर यह MSDN लेख () बताता है कि ड्राइव के लिए इष्टतम बफर आकार जानने के लिए ब्लॉक आकार कैसे प्राप्त करें।

वैसे भी, std :: ofstream एक आवरण है और I / O संचालन पर अवरुद्ध हो सकता है। ध्यान रखें कि पूरे एन-गीगाबाइट सरणी का पता लगाने में भी कुछ समय लगता है। जब आप एक छोटा बफर लिख रहे होते हैं, तो यह कैश में पहुंच जाता है और तेजी से काम करता है।


6

fstreams, C स्ट्रीम की तुलना में धीमी नहीं हैं, प्रति se हैं, लेकिन वे अधिक CPU का उपयोग करते हैं (विशेषकर यदि बफरिंग ठीक से कॉन्फ़िगर नहीं है)। जब एक सीपीयू संतृप्त होता है, तो यह I / O दर को सीमित करता है।

कम से कम MSVC 2015 कार्यान्वयन एक बार में एक आउटपुट बफर सेट नहीं होने पर 1 char की प्रतिलिपि बनाता है (देखें streambuf::xsputn)। इसलिए स्ट्रीम बफर (> 0) सेट करना सुनिश्चित करें

मुझे fstreamइस कोड का उपयोग करने के साथ 1500MB / s (मेरी 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";
}

मैं अन्य प्लेटफार्मों पर इस कोड (उबंटू, FreeBSD) की कोशिश की और कोई आई / ओ दर मतभेद देखा है, लेकिन एक CPU उपयोग 8 के बारे में अंतर: 1 ( fstreamइस्तेमाल किया 8 गुना अधिक सीपीयू )। तो कोई कल्पना कर सकता है, क्या मेरे पास एक तेज डिस्क fstreamथी, stdioसंस्करण की तुलना में लेखन जल्द ही धीमा हो जाएगा ।


3

मेमोरी-मैप्ड फ़ाइलों का उपयोग करने का प्रयास करें।


@ मेहरदाद लेकिन क्यों? क्योंकि यह एक मंच पर निर्भर समाधान है?
qehgt

3
नहीं ... यह इसलिए है क्योंकि तेजी से अनुक्रमिक फ़ाइल लेखन करने के लिए, आपको एक बार में बड़ी मात्रा में डेटा लिखने की आवश्यकता है। (कहते हैं, 2-MiB चंक्स शायद एक अच्छा शुरुआती बिंदु है।) मेमोरी मैप की गई फाइलें आपको ग्रैन्युलैरिटी को नियंत्रित करने की अनुमति नहीं देती हैं, इसलिए आप जो भी मेमोरी मैनेजर आपके लिए प्रीफ़ैच / बफर करने का निर्णय लेते हैं उसकी दया पर हैं। सामान्य तौर पर, मैंने उन्हें कभी भी सामान्य पढ़ने / लिखने के साथ ReadFileऔर ऐसे अनुक्रमिक उपयोग के लिए प्रभावी नहीं देखा है , हालांकि यादृच्छिक अभिगम के लिए वे बेहतर हो सकते हैं।
user541686

लेकिन स्मृति-मैपिंग फ़ाइलों का उपयोग ओएस द्वारा पेजिंग के लिए किया जाता है, उदाहरण के लिए। मुझे लगता है कि यह डेटा को पढ़ने / लिखने के लिए एक उच्च अनुकूलित (गति के संदर्भ में) तरीका है।
15

7
@ मिस्टिक: लोग बहुत सी बातें गलत हैं, जो 'जानते हैं'
बेन वोइगट

1
@qehgt: यदि कुछ भी, पेजिंग क्रमिक पहुँच की तुलना में यादृच्छिक पहुँच के लिए बहुत अधिक अनुकूलित है। एक ही ऑपरेशन में 1 मेगाबाइट डेटा पढ़ने की तुलना में 1 पेज डेटा पढ़ना बहुत धीमा है।
user541686

3

यदि आप एक्सप्लोरर में डिस्क ए से डिस्क बी तक कुछ कॉपी करते हैं, तो विंडोज डीएमए को नियोजित करता है। इसका मतलब है कि अधिकांश प्रतिलिपि प्रक्रिया के लिए, सीपीयू मूल रूप से डिस्क नियंत्रक को बताने के अलावा और कुछ नहीं करेगा, जहां डेटा डाला जाए, और डेटा प्राप्त किया जाए, श्रृंखला में एक पूरे कदम को नष्ट कर दिया जाए, और एक जो बड़ी मात्रा में स्थानांतरित करने के लिए बिल्कुल भी अनुकूलित नहीं है। डेटा का - और मेरा मतलब है हार्डवेयर।

क्या आप कर सीपीयू एक बहुत शामिल है। मैं आपको "[[] भाग भरने के लिए कुछ गणनाओं की ओर संकेत करना चाहता हूं।" जो मुझे लगता है कि जरूरी है। आप एक [] उत्पन्न करते हैं, फिर आप एक [] से आउटपुट बफर पर कॉपी करते हैं (अर्थात जो कुछ भी है :: लिखते हैं), फिर आप फिर से लिखते हैं, आदि।

क्या करें? बहु सूत्रण! (मुझे आशा है कि आपके पास एक मल्टी-कोर प्रोसेसर है)

  • कांटा।
  • [] डेटा उत्पन्न करने के लिए एक थ्रेड का उपयोग करें
  • एक [] से डिस्क पर डेटा लिखने के लिए दूसरे का उपयोग करें
  • आपको दो सरणियों a1 [] और a2 [] और उनके बीच स्विच की आवश्यकता होगी
  • आपको अपने थ्रेड्स (सेमाफ़ोर्स, संदेश कतार, आदि) के बीच किसी प्रकार के सिंक्रनाइज़ेशन की आवश्यकता होगी
  • WriteFile की तरह, निम्न स्तर, असंबद्ध, फ़ंक्शंस का उपयोग करें द्वारा उल्लिखित फ़ंक्शन की

1

यदि आप धाराओं को दर्ज करने के लिए तेजी से लिखना चाहते हैं तो आप पठन बफर को बड़ा कर सकते हैं:

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

इसके अलावा, जब फ़ाइलों के लिए बहुत सारा डेटा लिखना कभी-कभी शारीरिक रूप से फ़ाइल के आकार को तार्किक रूप से विस्तारित करने के लिए तेज़ होता है , इसका कारण यह है कि जब फ़ाइल को विस्तारित करते समय फ़ाइल सिस्टम इसे लिखने से पहले नए स्थान को शून्य नहीं करता है। यह तार्किक रूप से फ़ाइल का विस्तार करने के लिए भी स्मार्ट है, वास्तव में आपको बहुत सारी फ़ाइल सीमाओं को रोकने की आवश्यकता है। लॉजिकल फ़ाइल एक्सटेंशन को XFS सिस्टम पर कॉल करके SetFileValidDataया उसके xfsctlसाथ विंडोज पर समर्थित है XFS_IOC_RESVSP64


0

जीएनयू / लिनक्स और मिंगवॉव में जीसीसी में मेरे कार्यक्रम को संकलित करना में जीत 7 और विन XP और काम अच्छा

आप मेरे प्रोग्राम का उपयोग कर सकते हैं और एक file० जीबी फ़ाइल बनाने के लिए सिर्फ ३३ लाइन को बदल सकते हैं

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

जब प्रोग्राम से बाहर निकलें तो फ़ाइल नष्ट हो जाएगी तब फ़ाइल को चलाएं जब वह चल रही हो

कार्यक्रम है कि आप सिर्फ कार्यक्रम बदलना चाहते हैं

firt एक विंडोज़ प्रोग्राम है और दूसरा GNU / लिनक्स के लिए है

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.