tutorial - the c++ programming language




খুব দ্রুত একটি C++ মধ্যে বাইনারি ফাইল লেখা (8)

আমি আমার এসএসডি (কঠিন রাষ্ট্র ড্রাইভ) উপর বিশাল পরিমাণে তথ্য লিখতে চেষ্টা করছি। এবং বিশাল পরিমাণে আমি 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();
}

ভিজ্যুয়াল স্টুডিও ২010 এবং সম্পূর্ণ অপটিমাইজেশান এবং উইন্ডোজ 7 এর অধীনে চালিত এই প্রোগ্রামটি প্রায় ২0 এমবি / এস এর মধ্যে সর্বাধিক। সত্যিই আমাকে বিরক্ত করে যে উইন্ডোজ অন্য এসএসডি থেকে 150 এমবি / এস এবং 200 এমবি / এস এর মাঝে এসএসডি থেকে ফাইল কপি করতে পারে। তাই অন্তত 7 বার দ্রুত। তাই আমি মনে করি আমি দ্রুত যেতে সক্ষম হতে হবে।

কোন ধারনা আমি কিভাবে আমার লেখা গতি আপ করতে পারেন?


আপনি পরিবর্তে FILE* ব্যবহার করতে পারেন, এবং আপনি অর্জন করেছেন কর্মক্ষমতা পরিমাপ? fstream পরিবর্তে fwrite/write ব্যবহার করার কয়েকটি বিকল্প হল:

#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 ব্যবহার করার সিদ্ধান্ত 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 দেখতে পরামর্শ হবে। যে আপনার উত্তর হতে পারে। একবার আমাকে ডাটাবেসের মধ্যে সঞ্চয় করার জন্য ২0GB ফাইলটি প্রক্রিয়া করতে হয়েছিল এবং ফাইলটিকে এমনকি খোলার মতোও নয়। তাই Moemory মানচিত্র ব্যবহার করার জন্য সমাধান। আমি যদিও Python যে করেনি।


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

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

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

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

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

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

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

প্রোগ্রামটি প্রস্থান করার সময় ফাইলটি নষ্ট হয়ে যাবে এবং এটি চলমান অবস্থায় ফাইলটি পরীক্ষা করে দেখুন

প্রোগ্রাম আপনি চান শুধু প্রোগ্রাম পরিবর্তন করতে হবে

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

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

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


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

এছাড়াও নোট করুন:

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

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

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 ++ প্রবাহটি যত দ্রুত কাজ করছে ততক্ষণ অন্তর্নিহিত লাইব্রেরি অনুমতি দেবে।

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

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

সম্পাদন করা

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

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

এই কোন পার্থক্য তৈরি।

দ্রষ্টব্য : যদি আপনি একটি স্বাভাবিক ড্রাইভ থাকে তবে আমার ড্রাইভটি একটি এসএসডি ড্রাইভ যা উপরে দুটি কৌশলগুলির মধ্যে একটি পার্থক্য দেখতে পারে। কিন্তু হিসাবে আমি অ বাফারিং এবং বাফারিং (বাফার আকার বেশী বড় অংশ writting যখন) কোন পার্থক্য করা।

সম্পাদনা 2:

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

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

    output << input.rdbuf();
}

এই কাজটি করেছেন:

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

আমি শুধু 36 সেকেন্ডে 8 গিগাবাইট টাইম করেছি, যা প্রায় 220 এমবি / এস এবং আমার এসএসডি থেকে সর্বাধিক মনে হয়। এছাড়াও নোট মূল্য, প্রশ্ন কোডটি একটি কোর 100% ব্যবহার করে, যখন এই কোডটি শুধুমাত্র 2-5% ব্যবহার করে।

ধন্যবাদ সবাই সবার জন্য।

আপডেট : 5 বছর পাস হয়েছে। কম্পাইলার, হার্ডওয়্যার, লাইব্রেরি এবং আমার প্রয়োজনীয়তা পরিবর্তন হয়েছে। এজন্য আমি কোডে কিছু পরিবর্তন করেছি এবং কিছু পরিমাপ করেছি।

প্রথমে কোডটি আপ করুন:

#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 -std = c ++ 11-march = native-O3
  • ডেস্কটপ, কোর আই 7, এসএসডি, উইন্ডোজ 10, ভিজ্যুয়াল স্টুডিও 2017 সংস্করণ 15.3.1 / অক্স / ওবি 2 / ওআই / অ্যাট / জিটি / জিএল / জি

যা নিম্নলিখিত পরিমাপ (1 এমবি জন্য মান ditching পরে, তারা সুস্পষ্ট outliers ছিল) দিয়েছেন: উভয় option1 এবং option3 আমার এসএসডি সর্বোচ্চ আউট। আমি এই দেখতে প্রত্যাশা ছিল না, কারণ option2 আমার মেশিনে তারপর দ্রুততম কোড ব্যবহার করা হয়।

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


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

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

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

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

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


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


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

সময় লাইন তাকান:

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

'এফ' বাফার ভর্তি করার সময়কে প্রতিনিধিত্ব করে এবং 'W' ডিস্কে বাফার লেখার জন্য সময় উপস্থাপন করে। তাই লেখার বাফার ফাইলের মধ্যে সময় নষ্ট করার সমস্যা। তবে, পৃথক থ্রেডে লেখার প্রয়োগ করে আপনি পরবর্তী বাফারটি ঠিক এইভাবে ভর্তি শুরু করতে পারেন:

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

F - প্রথম বাফার ভর্তি
চ - দ্বিতীয় বাফার ভর্তি
ডাব্লু - ফাইল 1 ম বাফার লেখা
w - ফাইল থেকে দ্বিতীয় বাফার লেখা
_ - অপারেশন সম্পন্ন করার সময় অপেক্ষা করুন

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

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

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

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

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

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







io