সি ++ এ খুব দ্রুত একটি বাইনারি ফাইল লেখা


241

আমি আমার এসএসডি (সলিড স্টেট ড্রাইভ) এর উপরে প্রচুর পরিমাণে ডেটা লেখার চেষ্টা করছি। এবং বিশাল পরিমাণে আমার মানে 80 জিবি।

সমাধানের জন্য আমি ওয়েবটি ব্রাউজ করেছিলাম, তবে আমি যে সেরাটি সামনে এসেছি তা হ'ল:

#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 এর অধীনে চালিত এই প্রোগ্রামটি প্রায় 20 এমবি / সেকেন্ডে বাড়িয়ে তোলে out আসলে আমাকে বিরক্ত করার বিষয়টি হ'ল উইন্ডোজ অন্য এসএসডি থেকে এই এসএসডি থেকে 150MB / s এবং 200MB / s এর মধ্যে কোথাও ফাইলগুলি অনুলিপি করতে পারে। কমপক্ষে 7 বার দ্রুত। সে কারণেই আমার মনে হয় আমার আরও দ্রুত যেতে হবে।

কোনও ধারণা কীভাবে আমি আমার লেখার গতি বাড়িয়ে তুলতে পারি?


11
আপনার টাইমিংয়ের ফলাফলগুলি কোনও []] পূরণ করার জন্য আপনার গণনা করতে সময় সময়কে বাদ দেয়?
ক্যাচমেফিটআউটরি

7
আমি আসলে এই কাজ আগে করেছি। সহজ ব্যবহার করে fwrite()আমি প্রায় 80% শিখর লেখার গতি পেতে পারি। কেবলমাত্র FILE_FLAG_NO_BUFFERINGআমিই সর্বদা সর্বোচ্চ গতি পেতে সক্ষম হয়েছি।
রহস্যময়

10
আমি নিশ্চিত না যে আপনার ফাইলটি লেখার সাথে এসএসডি-থেকে-এসএসডি অনুলিপি করার সাথে তুলনা করা ন্যায়সঙ্গত। এটি ভালভাবে হতে পারে যে এসএসডি-থেকে-এসএসডি নিম্ন স্তরে কাজ করে, সি ++ লাইব্রেরি এড়ানো বা সরাসরি মেমোরি অ্যাক্সেস (ডিএমএ) ব্যবহার করে। কোনও কিছু অনুলিপি করা এলোমেলো অ্যাক্সেস ফাইলে স্বেচ্ছাচারিত মান লিখার মতো নয়।
ইগোর এফ।

4
@ আইগোরএফ: এটি কেবলমাত্র অনুমান; এটি পুরোপুরি ন্যায্য তুলনা (যদি অন্য কিছুই না হয় তবে ফাইল লেখার পক্ষে)। উইন্ডোতে কোনও ড্রাইভ জুড়ে অনুলিপি করা কেবল পঠন-লিখন; অভিনব / জটিল / ভিন্ন কিছুই নীচে চলছে।
ব্যবহারকারী541686

5
@ ম্যাক্সিম ইয়েগরোশকিন: লিঙ্ক করুন বা এটি ঘটেনি। : পি
ব্যবহারকারী541686

উত্তর:


233

এটি কাজটি করেছে (২০১২ সালে):

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

আমি সবে মাত্র ৩ জিবি টাইম করেছি sec জিএস, যা প্রায় ২২০ এমবি / সে এবং আমি মনে করি যে আমার এসএসডিটি সর্বাধিক বাড়িয়েছে। এছাড়াও লক্ষণীয়, প্রশ্নের কোডটিতে একটি কোর ব্যবহৃত হয়েছে 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 (একটি নতুন প্রয়োজনীয়তা) দিয়ে সংকলন করে। আমি দুটি সেটআপ দিয়ে কোডটি চালিয়েছি:

  • ল্যাপটপ, কোর আই 7, এসএসডি, উবুন্টু 16.04, জি ++ সংস্করণ 7.2.0 সহ -এসডি = সি ++ 11 -মার্চ = নেটিভ -O3
  • ডেস্কটপ, কোর আই 7, এসএসডি, উইন্ডোজ 10, ভিজ্যুয়াল স্টুডিও 2017 সংস্করণ 15.3.1 সাথে / অক্স / ওবি 2 / ওআই / ওটি / জিটি / জিএল / জিআই

যা নিম্নলিখিত পদক্ষেপগুলি দিয়েছে (1MB এর জন্য মানগুলি খোলার পরে, কারণ তারা সুস্পষ্ট আউটলিয়ার ছিল): আমার এসএসডি থেকে বিকল্প সময় 1 এবং অপশন 3 উভয়ই সর্বোচ্চ। আমি এটি দেখার আশা করিনি, কারণ অপশন 2 তখন আমার পুরানো মেশিনের সবচেয়ে দ্রুততম কোড হিসাবে ব্যবহৃত হত।এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন

টিএল; ডিআর : আমার পরিমাপটি std::fstreamওভার ব্যবহারের জন্য নির্দেশ করে FILE


8
হ্যাঁ, এটি আমি প্রথম চেষ্টা করলাম। FILE*স্রোতের চেয়ে দ্রুত। আমি যে কোনওভাবেই I / O আবদ্ধ থাকা উচিত ছিলাম যেহেতু আমি এ জাতীয় পার্থক্য আশা করিনি।
রহস্যময়

12
আমরা কি এই উপসংহারে পৌঁছাতে পারি যে সি-স্টাইলের আই / ওটি (আশ্চর্যরূপে) সি ++ স্ট্রিমের চেয়ে অনেক দ্রুত গতিতে রয়েছে?
শেচেপুরিন

21
@ এস চেপুরিন: আপনি যদি পেডেন্টিক হন তবে সম্ভবত তা নয়। আপনি যদি ব্যবহারিক হয়ে থাকেন তবে সম্ভবত হ্যাঁ। :)
user541686

10
আপনি দয়া করে ব্যাখ্যা করতে পারেন (আমার মতো সি ++ ডানদের জন্য) দুটি পদ্ধতির মধ্যে পার্থক্য এবং কেন এটি একটি আসলটির চেয়ে এত দ্রুত কাজ করে?
মাইক চেম্বারলাইন

11
প্রিপেন্ডিং কি ios::sync_with_stdio(false);স্ট্রিম সহ কোডের জন্য কোনও পার্থক্য করে? আমি কেবল কৌতূহলী যে এই লাইনটি ব্যবহার করার মধ্যে এবং না এর মধ্যে কত বড় পার্থক্য রয়েছে, তবে কোণার কেসটি পরীক্ষা করার জন্য আমার কাছে দ্রুত পর্যাপ্ত ডিস্ক নেই। এবং যদি কোন বাস্তব পার্থক্য আছে।
আর্টুর সিজ্জাকা

24

ক্রমে নিম্নলিখিত চেষ্টা করুন:

  • ছোট বাফার আকার। একসাথে ~ 2 MiB লেখা ভাল শুরু হতে পারে। আমার শেষ ল্যাপটপে, 512 ডলার কিবিটি মিষ্টি স্পট ছিল তবে আমি এখনও আমার এসএসডি তে পরীক্ষা করিনি।

    দ্রষ্টব্য: আমি লক্ষ করেছি যে খুব বড় বাফারগুলির কর্মক্ষমতা হ্রাস হয় । আমি আগে 512-কিবি বাফারের পরিবর্তে 16-MiB বাফার ব্যবহার করে গতির ক্ষতি লক্ষ্য করেছি।

  • ব্যবহারের _open(অথবা _topenযদি আপনি Windows-সঠিক হতে চান) ফাইল খোলার জন্য, তারপর ব্যবহার _write। এটি সম্ভবত প্রচুর পরিমাণে বাফারিং এড়াবে, তবে এটি নির্দিষ্ট নয়।

  • উইন্ডোজ-নির্দিষ্ট ফাংশন CreateFileএবং এর মতো ব্যবহার করে WriteFile। এটি স্ট্যান্ডার্ড লাইব্রেরিতে কোনও বাফার এড়াবে।


অনলাইনে পোস্ট করা কোনও মানদণ্ডের ফলাফল পরীক্ষা করুন। যে কোনও ধরণের শালীন মাধ্যমে আউটপুট পেতে আপনার 4kB র 32 অথবা ততোধিক কিউর গভীরতা সহ অন্যথায় 512 কে বা উচ্চতর লেখার দরকার রয়েছে।
বেন ভয়েগট

@ বেনভয়েট: হ্যাঁ, এটি আমার সাথে 512 কিবি বলার সাথে মিলে যায়। :)
user541686

হ্যাঁ. আমার অভিজ্ঞতা থেকে, ছোট বাফার আকারগুলি সাধারণত সর্বোত্তম হয়। ব্যতিক্রমটি যখন আপনি ব্যবহার করছেন FILE_FLAG_NO_BUFFERING- এতে বৃহত্তর বাফারগুলি আরও ভাল থাকে। যেহেতু আমার মনে FILE_FLAG_NO_BUFFERINGহয় এটি অনেক বেশি ডিএমএ।
রহস্যময়

22

আমি স্ট্যান্ড :: স্ট্রিম / ফাইল / ডিভাইসের মধ্যে কোনও পার্থক্য দেখছি না। বাফারিং এবং অ বাফারিংয়ের মধ্যে।

আরও মনে রাখবেন:

  • এসএসডি তারা ভরাট হওয়ার সাথে সাথে (কম ট্রান্সফার রেট) কমিয়ে দিতে "ঝোঁক" চালিত করে।
  • বয়স্ক হওয়ার সাথে সাথে (কম ওয়ার্কিং বিটের কারণে) এসএসডি ড্রাইভগুলি "প্রবণতা" কমিয়ে দেয় (কম ট্রান্সফার রেট) দেয়।

আমি কোডটি 63 সেকেন্ডে রান দেখছি।
সুতরাং এর স্থানান্তর হার: 260M / s (আমার এসএসডি আপনার চেয়ে কিছুটা দ্রুত দেখায়)।

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

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

আমি স্টাইল :: 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);

}

সুতরাং সি ++ স্ট্রিম তত দ্রুত কাজ করছে যা অন্তর্নিহিত গ্রন্থাগারটি অনুমতি দেবে।

তবে আমি মনে করি যে এটি কোনও ওএস-এর উপরে নির্মিত কোনও অ্যাপ্লিকেশনটির সাথে ওএসের তুলনা করা অন্যায্য air অ্যাপ্লিকেশনটি কোনও অনুমান করতে পারে না (এটি জানে না যে ড্রাইভগুলি এসএসডি হয়) এবং এটি হস্তান্তর করার জন্য ওএসের ফাইল প্রক্রিয়া ব্যবহার করে।

যদিও ওএসকে কোনও অনুমান করার দরকার নেই। এটি জড়িত ড্রাইভগুলির প্রকারগুলি বলতে এবং ডেটা স্থানান্তর করার জন্য সর্বোত্তম কৌশলটি ব্যবহার করতে পারে। এক্ষেত্রে মেমরি ট্রান্সফারে সরাসরি মেমরি। এমন একটি প্রোগ্রাম লেখার চেষ্টা করুন যা মেমরির 1 অবস্থান থেকে অন্য জায়গায় 80G অনুলিপি করে এবং দেখুন কী দ্রুত।

সম্পাদন করা

নিম্ন স্তরের কলগুলি ব্যবহার করতে আমি আমার কোড পরিবর্তন করেছি:
অর্থাত্ কোনও বাফারিং নেই।

#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:

আপনি কি সি ++ এ ফাইলগুলি অনুলিপি করার দ্রুততম পদ্ধতির চেষ্টা করেছেন?

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

    output << input.rdbuf();
}

5
আমি ডাউনভোট করিনি, তবে আপনার বাফার আকারটি খুব ছোট। ওপি ব্যবহার করছে একই 512 এমবি বাফার দিয়ে আমি এটি করেছি এবং আমি 90 এমবি / এস এর সাথে স্ট্রিম সহ 20 এমবি / এস পাই FILE*
রহস্যময়

লেখার মাধ্যমেও আপনার পথ (ক, আকারের (স্বাক্ষরযুক্ত দীর্ঘ দীর্ঘ), আকার, প্রবাহ)); পরিবর্তিত (এ, 1, আকার * আকারের (স্বাক্ষরযুক্ত দীর্ঘ দীর্ঘ), পি ফাইল) এর পরিবর্তে); আমাকে 220MB / s লেখার জন্য M৪ এমবি পরিমাণের সাথে দেয়
ডমিনিক হফার

2
@ মিস্টিয়াল: এটি আমার অবাক করে দেয় যে বাফার আকারটি একটি পার্থক্য করে (যদিও আমি আপনাকে বিশ্বাস করি)। আপনার প্রচুর ছোট লেখার সময় বাফারটি কার্যকর হয় যাতে অন্তর্নিহিত ডিভাইসটি অনেক অনুরোধে বিরক্ত না হয়। কিন্তু যখন আপনি বিশাল অংশ লিখছেন / লেখার সময় কোনও বাফারের প্রয়োজন নেই (একটি ব্লকিং ডিভাইসে)। যেমন ডেটা সরাসরি অন্তর্নিহিত ডিভাইসে প্রেরণ করা উচিত (এইভাবে বাফার দ্বারা পাস করে)। যদিও আপনি যদি কোনও পার্থক্য দেখেন তবে এটি এর বিরোধিতা করবে এবং আমার বিস্মিত করে তুলবে কেন লেখাটি আসলে মোটেই বাফার ব্যবহার করছে।
মার্টিন ইয়র্ক

2
সেরা সমাধানটি বাফারের আকার বাড়াতে নয় তবে বাফারটি সরিয়ে ফেলা এবং লিখিতভাবে তথ্যটি অন্তর্নিহিত ডিভাইসে সরাসরি পাস করা।
মার্টিন ইয়র্ক

1
@ মিস্টিয়াল: 1) কোনও ছোট অংশ নেই => এটি সর্বদা যথেষ্ট বড় (এই উদাহরণে)। এই ক্ষেত্রে খণ্ডগুলি 512 এম 2) এটি একটি এসএসডি ড্রাইভ (আমার এবং ওপি উভয়) তাই এটির কোনওটিই প্রাসঙ্গিক নয়। আমি আমার উত্তর আপডেট করেছি।
মার্টিন ইয়র্ক

13

সেরা সমাধান হ'ল ডাবল বাফারিং সহ একটি অ্যাসিঙ্ক রচনাটি বাস্তবায়ন করা।

সময়রেখায় দেখুন:

------------------------------------------------>
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 বাফার ফাইল লিখুন
_ অপারেশন শেষ হওয়ার পরে অপেক্ষা করুন

বাফার পূরণের ক্ষেত্রে আরও জটিল গণনার প্রয়োজন হয় (সুতরাং, আরও সময়) buff আমি সর্বদা একটি সিসিক্যুয়াল স্ট্রিম রাইটার ক্লাস প্রয়োগ করি যা অ্যাসিঙ্ক্রোনাস রচনাটি ভিতরে লুকিয়ে রাখে, তাই শেষ-ব্যবহারকারীর জন্য ইন্টারফেসটির কেবল রচনা ফাংশন (গুলি) থাকে।

এবং বাফার আকারটি অবশ্যই ডিস্ক ক্লাস্টারের আকারের একাধিক হতে হবে। অন্যথায়, আপনি 2 সংলগ্ন ডিস্ক ক্লাস্টারে একটি একক বাফার লিখে খারাপ পারফরম্যান্সটি শেষ করবেন।

শেষ বাফার লেখা।
আপনি যখন শেষবারের জন্য রাইটিং ফাংশনটি কল করবেন তখন আপনাকে নিশ্চিত করতে হবে যে বর্তমান বাফারটি পূরণ হচ্ছে তা ডিস্কেও লেখা উচিত। সুতরাং CSequentialStreamWriter এর একটি পৃথক পদ্ধতি থাকা উচিত, যাক Finalize (চূড়ান্ত বাফার ফ্লাশ), যা ডিস্কে ডেটার শেষ অংশে লেখা উচিত।

ত্রুটি পরিচালনা.
কোডটি ২ য় বাফার পূরণ করা শুরু করে এবং 1 মটি পৃথক থ্রেডে লেখা হচ্ছে, তবে কোনও কারণে লিখতে ব্যর্থ হয়, মূল থ্রেডটি সেই ব্যর্থতা সম্পর্কে সচেতন হওয়া উচিত।

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

আসুন ধরে নেওয়া যাক একটি CSequentialStreamWriter এর ইন্টারফেসটিতে রোল ফাংশনটি রিটার্ন দেয় বা একটি ব্যতিক্রম ছুঁড়ে ফেলেছে, এইভাবে একটি পৃথক থ্রেডে ত্রুটি রয়েছে, আপনাকে সেই অবস্থাটি মনে রাখতে হবে, সুতরাং পরের বার আপনি যখন মুখ্য থ্রেডে লিখুন বা ফিনালাইজ করবেন তখন পদ্ধতিটি ফিরে আসবে মিথ্যা বা একটি ব্যতিক্রম নিক্ষেপ করবে। এবং ব্যর্থ হওয়ার পরে আপনি কিছু ডেটা লিখে রেখেছিলেন, এমনকী আপনি কোন বাফারটি পূরণ করা বন্ধ করেছেন তা আসলেই গুরুত্বপূর্ণ নয় - সম্ভবত ফাইলটি দূষিত এবং অকেজো হয়ে যাবে।


3
I / O সম্পাদন করা গণনার সাথে সমান্তরাল একটি খুব ভাল ধারণা, তবে উইন্ডোজটিতে আপনার এটির জন্য থ্রেড ব্যবহার করা উচিত নয়। পরিবর্তে, "ওভারল্যাপড আই / ও" ব্যবহার করুন, যা আই / ও কল চলাকালীন আপনার কোনও থ্রেডকে ব্লক করে না। এর অর্থ হল আপনাকে থ্রেড সিঙ্ক্রোনাইজেশন সম্পর্কে সবেমাত্র চিন্তা করতে হবে (এটি ব্যবহার করে সক্রিয় আই / ও অপারেশন রয়েছে এমন কোনও বাফারটি অ্যাক্সেস করবেন না)।
বেন ভয়েগট

11

আমি ফাইল ম্যাপিং চেষ্টা করার পরামর্শ দেব । আমি mmapঅতীতে ইউনিক্স পরিবেশে ব্যবহার করেছি এবং আমি যে উচ্চতর পারফরম্যান্স অর্জন করতে পেরেছি তাতে মুগ্ধ হয়েছি


1
@ নাপ্পলি এটি এখনও একটি কার্যকরী, দক্ষ এবং আকর্ষণীয় সমাধান মনে রাখবেন।
ইয়াম মার্কোভিচ

স্ট্যাকওভারফ্লো.com/a/2895799/220060 এমএএমএপ-এর পক্ষের সম্পর্কে। বিশেষত নোট করুন "ফাইলের খাঁটি অনুক্রমিক অ্যাক্সেসের জন্য, এটি সর্বদা আরও ভাল সমাধান নয় [...]" এছাড়াও স্ট্যাকওভারফ্লো / প্রশ্নগুলি / 72২646471১ , এটি কার্যকরভাবে বলেছে যে 32-বিট সিস্টেমে আপনি 2 বা সীমাবদ্ধ রয়েছেন 3 জিবি। - যাইহোক, আমি যে উত্তরটি হ্রাস করেছিলাম তা আমি নই।
স্পষ্টত

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 জিবি ফাইল প্রক্রিয়া করতে হয়েছিল এটি ডাটাবেসে সংরক্ষণ করার জন্য, এবং ফাইলটি খোলার মতো নয়। তাই সমাধানটি মোমোরি ম্যাপটি ব্যবহার করতে হবে। আমি Pythonযদিও এটি করেছি।


প্রকৃতপক্ষে, FILE*একই 512 এমবি বাফার ব্যবহার করে মূল কোডের একটি সরাসরি-ফরওয়ার্ড সমান পুরো গতি পায়। আপনার বর্তমান বাফার খুব ছোট।
রহস্যময়

1
@ রহস্যময় তবে এটি একটি উদাহরণ।
সাইবারটেক্সট্রন

বেশিরভাগ সিস্টেমে, 2 স্ট্যান্ডার্ড ত্রুটির সাথে মিলে যায় তবে এটির STDERR_FILENOপরিবর্তে আপনি এটি ব্যবহার করার পরামর্শ দিচ্ছেন 2। আর একটি গুরুত্বপূর্ণ বিষয় হ'ল আপনি যে কোনও সম্ভাব্য ত্রুটি পেতে পারেন তা হ'ল EINTR আপনি যখন কোনও বাধা সংকেত পান তখন এটি কোনও ত্রুটি নয় এবং আপনার কেবল আবার চেষ্টা করা উচিত।
পেইম্যান

6

ওপেন () / রাইটিং () / ক্লোজ () এপিআই কলগুলি ব্যবহার করে আউটপুট বাফার আকারটি ব্যবহার করে দেখুন। আমি বোঝাতে চাইছি একবারে পুরো "বহু-বহু-বাইটস" বাফারটি পাস করবেন না, কয়েকটা লেখেন (যেমন, টোটালনামবাইটস / আউটবুফারসাইজ)। আউটবুফারসাইজ 4096 বাইট থেকে মেগাবাইট পর্যন্ত হতে পারে।

আরেকবার চেষ্টা করুন - WinAPI ওপেন ফাইল / ক্রিয়েটফিল ব্যবহার করুন এবং এই এমএসডিএন নিবন্ধটি বাফারিং বন্ধ করতে (FILE_FLAG_NO_BUFFERING) ব্যবহার করুন। এবং WritFile () এর এই এমএসডিএন নিবন্ধটি দেখায় যে কীভাবে ড্রাইভের জন্য সর্বোত্তম বাফার আকারটি জানতে ব্লক আকার পেতে হয়।

যাইহোক, std :: ofstream একটি মোড়ক এবং সেখানে I / O ক্রিয়াকলাপ অবরুদ্ধ হতে পারে। মনে রাখবেন যে পুরো এন-গিগাবাইট অ্যারেটি অনুসরণ করতে কিছুটা সময়ও লাগে। আপনি যখন একটি ছোট বাফার লিখছেন, এটি ক্যাশে যায় এবং দ্রুত কাজ করে।


6

fstreamগুলি সি স্ট্রিমের চেয়ে ধীরে ধীরে নয়, তবে তারা বেশি সিপিইউ ব্যবহার করে করে (বিশেষত যদি বাফারিং সঠিকভাবে কনফিগার করা না থাকে)। যখন কোনও সিপিইউ পরিপূর্ণ হয়, এটি আই / ও হারকে সীমাবদ্ধ করে।

কোনও স্ট্রিম বাফার সেট না করা অবস্থায় এমএসভিসি 2015 বাস্তবায়ন একবারে আউটপুট বাফারে 1 টি অনুলিপি করে (দেখুন streambuf::xsputn)। সুতরাং একটি স্ট্রিম বাফার (> 0) সেট করা নিশ্চিত করুন

fstreamএই কোডটি ব্যবহার করে আমি 1500MB / গুলি (আমার এম 2 এসএসডি-র পূর্ণ গতি) লেখার গতি পেতে পারি :

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

আমি অন্য প্ল্যাটফর্মগুলিতে (উবুন্টু, ফ্রিবিএসডি) এই কোডটি চেষ্টা করে দেখেছি যে আমি / ও হারের কোনও পার্থক্য নেই, তবে সিপিইউ ব্যবহারের পার্থক্য প্রায় 8: 1 ( 8 গুণ বেশি সিপিইউfstream ব্যবহৃত হয়েছে )। সুতরাং যে কেউ ভাবতে পারেন, আমার কাছে যদি একটি দ্রুত ডিস্ক fstreamথাকত তবে লেখাগুলি stdioসংস্করণটির চেয়ে শীঘ্রই ধীর হয়ে যেত ।


3

মেমরি-ম্যাপযুক্ত ফাইলগুলি ব্যবহার করার চেষ্টা করুন।


@ মেহরদাদ কিন্তু কেন? কারণ এটি একটি প্ল্যাটফর্ম নির্ভর সমাধান?
কিহিগট

3
না ... কারণ দ্রুত ক্রমিক ফাইল লেখার জন্য আপনাকে একবারে প্রচুর পরিমাণে ডেটা লিখতে হবে। (বলুন, 2-মাইবি খণ্ডগুলি সম্ভবত একটি ভাল সূচনার পয়েন্ট)) মেমরি ম্যাপযুক্ত ফাইলগুলি আপনাকে গ্রানুলারিটি নিয়ন্ত্রণ করতে দেয় না, তাই মেমোরি পরিচালক আপনার জন্য প্রিফেট / বাফার ঠিক করার সিদ্ধান্ত নিয়েই থাকেন। সাধারণভাবে, আমি এগুলি কখনই সাধারণ পড়া / লেখার মতো কার্যকর ReadFileএবং ক্রমযুক্ত অ্যাক্সেসের জন্য কার্যকর হতে দেখিনি , যদিও এলোমেলো অ্যাক্সেসের জন্য সেগুলি আরও ভাল হতে পারে।
ব্যবহারকারী541686

উদাহরণস্বরূপ, মেমরি-ম্যাপযুক্ত ফাইলগুলি ওএস দ্বারা পেজিংয়ের জন্য ব্যবহৃত হয়। আমি মনে করি এটি ডেটা পড়ার / লেখার একটি অত্যন্ত অনুকূল (গতির শর্তে) উপায়।
কিহিগট

7
@ রহস্যময়ী: লোকেরা "অনেক কিছুই" জানেন যা কেবল সাধারণ ভুল।
বেন ভয়েগট

1
@ কিহগট: যদি কিছু হয় তবে পেজিং ক্রমহীন অ্যাক্সেসের চেয়ে এলোমেলো অ্যাক্সেসের জন্য অনেক বেশি অনুকূলিত। একক ক্রিয়াকলাপে 1 মেগাবাইট ডেটা পড়ার চেয়ে 1 পৃষ্ঠার ডেটা পড়া অনেক ধীর
ব্যবহারকারী541686

3

আপনি যদি এক্সপ্লোরারটিতে ডিস্ক এ থেকে ডিস্ক বি তে কোনও কিছু অনুলিপি করেন তবে উইন্ডোজ ডিএমএ নিয়োগ করে। এর অর্থ হ'ল বেশিরভাগ অনুলিপি প্রক্রিয়াটির জন্য, সিপিইউ মূলত ডিস্ক নিয়ন্ত্রককে কোথায় রাখা উচিত তা জানাতে এবং তথ্য প্রাপ্তি, চেইনের পুরো পদক্ষেপটি মুছে ফেলার ব্যতীত অন্য কিছু করবে না, এবং এমন একটি যা বড় পরিমাণে সরানোর জন্য অনুকূল নয় ডেটা - এবং আমি হার্ডওয়্যার মানে।

কি আপনি কি করতে CPU- র অনেক কিছু জড়িত। আমি আপনাকে "[]" অংশ পূরণ করার জন্য কিছু গণনার দিকে নির্দেশ করতে চাই। যা আমি মনে করি অপরিহার্য। আপনি একটি []] উত্পন্ন করেন, তারপরে আপনি একটি [] থেকে একটি আউটপুট বাফারে অনুলিপি করেন (যা fstream :: লিখতে পারে তা বোঝায়), তারপরে আপনি আবার জেনারেট করবেন ইত্যাদি

কি করো? Multithreading! (আমি আশা করি আপনার কাছে একটি মাল্টি-কোর প্রসেসর রয়েছে)

  • মাল।
  • একটি [] ডেটা তৈরি করতে একটি থ্রেড ব্যবহার করুন
  • একটি [] থেকে ডিস্কে ডেটা লিখতে অন্যটি ব্যবহার করুন
  • আপনার দুটি অ্যারে এ 1 [] এবং এ 2 [] দরকার হবে এবং তাদের মধ্যে স্যুইচ করুন
  • আপনার থ্রেডগুলির মধ্যে কিছু ধরণের সিঙ্ক্রোনাইজেশন প্রয়োজন হবে (সেমফোরস, বার্তা সারি ইত্যাদি)
  • মেহরদাদ দ্বারা উল্লিখিত লিখনফিল ফাংশনের মতো নিম্ন স্তরের, আনফার্ডড, ফাংশনগুলি ব্যবহার করুন

1

আপনি যদি ফাইল স্ট্রিমগুলিতে দ্রুত লিখতে চান তবে আপনি স্ট্রিমটি রিড বাফারটিকে আরও বড় করতে পারবেন:

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

এছাড়াও, ফাইলগুলিতে প্রচুর ডেটা লেখার সময় কখনও কখনও শারীরিক পরিবর্তে ফাইলের আকার যুক্তিসঙ্গতভাবে প্রসারিত করা দ্রুত হয় , কারণ এটি যুক্তিযুক্তভাবে কোনও ফাইল প্রসারিত করার সময় ফাইল সিস্টেমটি লেখার আগে নতুন স্থানটি শূন্য করে না। আপনার প্রচুর ফাইলের প্রসার রোধ করার চেয়ে যুক্তিযুক্তভাবে ফাইলটি প্রসারিত করাও স্মার্ট। লজিকাল ফাইল এক্সটেনশনটি উইন্ডোজে কল করে SetFileValidDataবা এক্সএফএস সিস্টেমের xfsctlসাহায্যে সমর্থিত XFS_IOC_RESVSP64


0

IM- এ জিসিসি আমার প্রোগ্রাম কম্পাইল জিএনইউ / লিনাক্স এবং mingw জয়ের 7 এবং জয় XP এবং কাজ ভাল

আপনি আমার প্রোগ্রামটি ব্যবহার করতে পারেন এবং একটি 80 গিগাবাইট ফাইল তৈরি করতে কেবল লাইনটি 33 এ পরিবর্তন করুন

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

প্রোগ্রাম থেকে প্রস্থান করার সময় ফাইলটি নষ্ট হয়ে যাবে তখন ফাইলটি চলমান আছে কিনা তা পরীক্ষা করুন

আপনি যে প্রোগ্রামটি চান তা কেবল প্রোগ্রামটি পরিবর্তন করতে হবে

চমত্কার একটি হ'ল উইন্ডোজ প্রোগ্রাম এবং দ্বিতীয়টি জিএনইউ / লিনাক্সের জন্য

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

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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.