কী / মান স্টোর বিকাশ আধুনিক সি ++ এ


9

আমি ক্যাসান্দ্রার অনুরূপ একটি ডেটাবেস সার্ভার বিকাশ করছি।

সিতে বিকাশ শুরু হয়েছিল, কিন্তু ক্লাস ছাড়াই জিনিসগুলি খুব জটিল হয়ে ওঠে।

বর্তমানে আমি সি ++ 11 এ সমস্ত কিছু পোর্ট করেছি তবে আমি এখনও "আধুনিক" সি ++ শিখছি এবং প্রচুর বিষয়ে সন্দেহ আছে।

কী / মান জোড়া নিয়ে ডেটাবেস কাজ করবে। প্রতিটি জুটির আরও কিছু তথ্য থাকে - কখন তৈরি হয় যখন এটি শেষ হয় (0 টি মেয়াদ শেষ না হলে)। প্রতিটি জুড়ি অপরিবর্তনীয়।

কীটি হ'ল সি স্ট্রিং, মানটি শূন্য *, তবে কমপক্ষে মুহূর্তের জন্য আমি সি স্ট্রিংয়ের মতো মানটিও পরিচালনা করছি।

বিমূর্ত IListক্লাস আছে। এটি তিনটি শ্রেণী থেকে উত্তরাধিকারসূত্রে প্রাপ্ত

  • VectorList - সি ডায়নামিক অ্যারে - স্ট্যান্ড :: ভেক্টর এর অনুরূপ, তবে ব্যবহার realloc
  • LinkList - চেক এবং পারফরম্যান্স তুলনা জন্য তৈরি
  • SkipList - শেষ পর্যন্ত যে ক্লাসটি ব্যবহার করা হবে।

ভবিষ্যতে আমি Red Blackগাছও করতে পারি ।

প্রতিটি IListজোতে শূন্য বা আরও বেশি পয়েন্টার যুক্ত করে কী অনুসারে বাছাই করে।

যদি IListখুব দীর্ঘ হয়ে যায় তবে এটি একটি বিশেষ ফাইলে ডিস্কে সংরক্ষণ করা যায়। এই বিশেষ ফাইলটি ধরণের read only list

আপনার যদি কোনও কীটি অনুসন্ধান করতে হয়,

  • মেমরি প্রথম IListঅনুসন্ধান করা হয় ( SkipList, SkipListবা LinkList)।
  • তারপরে অনুসন্ধানের তারিখ অনুসারে বাছাই করা ফাইলগুলিতে প্রেরণ করা হয়
    (সর্বশেষতম ফাইল, প্রাচীনতম ফাইল - শেষ)।
    এই সমস্ত ফাইল স্মৃতিতে এমএমএপ-এড।
  • যদি কিছু না পাওয়া যায় তবে কীটি পাওয়া যায় না।

IListজিনিসগুলির বাস্তবায়ন সম্পর্কে আমার কোনও সন্দেহ নেই ।


বর্তমানে যা আমাকে বিস্মিত করছে তা অনুসরণ করছে:

জোড়াগুলি বিভিন্ন আকারের হয়, তাদের দ্বারা বরাদ্দ করা হয় new()এবং তারা তাদের std::shared_ptrদিকে নির্দেশ করে।

class Pair{
public:
    // several methods...
private:
    struct Blob;

    std::shared_ptr<const Blob> _blob;
};

struct Pair::Blob{
    uint64_t    created;
    uint32_t    expires;
    uint32_t    vallen;
    uint16_t    keylen;
    uint8_t     checksum;
    char        buffer[2];
};

"বাফার" সদস্যের পরিবর্তনশীল হ'ল বিভিন্ন আকারের। এটি কী + মান সঞ্চয় করে।
উদাহরণস্বরূপ যদি কীটি 10 ​​টি অক্ষর হয় এবং মানটি 10 ​​টি বাইট হয় তবে পুরো বস্তুটি হবে sizeof(Pair::Blob) + 20(দুটি নাল টার্মিনেটিং বাইটের কারণে বাফারের প্রাথমিক আকার 2 হবে)

এই একই লেআউটটি ডিস্কটিতেও ব্যবহৃত হয়, তাই আমি এরকম কিছু করতে পারি:

// get the blob
Pair::Blob *blob = (Pair::Blob *) & mmaped_array[pos];

// create the pair, true makes std::shared_ptr not to delete the memory,
// since it does not own it.
Pair p = Pair(blob, true);

// however if I want the Pair to own the memory,
// I can copy it, but this is slower operation.
Pair p2 = Pair(blob);

তবে এই ভিন্ন আকারটি সি ++ কোড সহ প্রচুর জায়গায় সমস্যা।

উদাহরণস্বরূপ আমি ব্যবহার করতে পারি না std::make_shared()। এটি আমার পক্ষে গুরুত্বপূর্ণ, কারণ আমার যদি 1 এম পেয়ার হয় তবে আমার 2 এম বরাদ্দ থাকবে।

অন্যদিকে, যদি আমি গতিশীল অ্যারেতে "বাফার" করি (উদাহরণস্বরূপ নতুন চর [123]), আমি এমএম্যাপ "কৌশল" হারাব, আমি কীটি পরীক্ষা করতে চাইলে আমি দুটি ডিरेফারেন্স করব এবং আমি একক পয়েন্টার যুক্ত করব - ক্লাসে 8 বাইট।

আমিও "পুল" থেকে সকল সদস্যদের করার চেষ্টা Pair::Blobমধ্যে Pair, তাই Pair::Blobশুধু বাফার হতে, কিন্তু আমি এটা পরীক্ষিত, এটা সম্ভবত কারণ প্রায় অবজেক্ট ডেটা কপি বেশ ধীর ছিল।

আমি আরও একটি পরিবর্তন সম্পর্কে ভাবছি যা হ'ল Pairশ্রেণিটি সরিয়ে নেওয়া এবং এটিকে প্রতিস্থাপন করা std::shared_ptrএবং সমস্ত পদ্ধতিগুলিতে ফিরে "ধাক্কা" দেওয়া Pair::Blob, তবে এটি আমাকে ভেরিয়েবল সাইজের Pair::Blobশ্রেণিতে সহায়তা করবে না ।

আমি আরও ভাবছি যে আরও সি ​​++ বান্ধব হওয়ার জন্য আমি কীভাবে অবজেক্ট ডিজাইনের উন্নতি করতে পারি।


সম্পূর্ণ উত্স কোডটি এখানে:
https://github.com/nmmmnu/HM3


2
আপনি std::mapবা ব্যবহার করবেন না কেন std::unordered_map? মানগুলি কিছু (কীগুলির সাথে সম্পর্কিত) কেন void*? আপনার সম্ভবত কোনও সময় তাদের ধ্বংস করা প্রয়োজন; কিভাবে এবং কখন? আপনি টেমপ্লেট ব্যবহার করবেন না কেন?
বেসিল স্টারিনকিভিচ

আমি স্টাডি :: ম্যাপ ব্যবহার করি না, কারণ আমি বিশ্বাস করি (বা কমপক্ষে চেষ্টা করুন) বর্তমান ক্ষেত্রে মানচিত্রে মানচিত্রের চেয়ে আরও ভাল কিছু করার জন্য। তবে হ্যাঁ আমি স্টেড :: ম্যাপটি মোড়ানোর জন্য কিছু সময় ভাবছি এবং পাশাপাশি এটি আইএলিস্ট হিসাবে সম্পাদন করতে পারি।
নিক

এলিয়েন্ট যেখানে থাকে IList::removeবা আইএলিস্ট ধ্বংস হয় তখন ডিওলোকেশন এবং কল-ডি-টর্স করা হয় । এটি অনেক সময় নেয় তবে আমি আলাদা থ্রেডে যাচ্ছি। এটি সহজ হবে কারণ আইলিস্ট std::unique_ptr<IList>যাইহোক হবে। সুতরাং আমি এটি নতুন তালিকার সাথে "স্যুইচ" করতে সক্ষম হব এবং পুরানো অবজেক্টটি কোথাও রাখি যেখানে আমি ডি-টর কল করতে পারি।
নিক

আমি টেমপ্লেট চেষ্টা করেছিলাম। এগুলি এখানে সর্বোত্তম সমাধান নয়, কারণ এটি ব্যবহারকারীর লাইব্রেরি নয়, কী সর্বদা থাকে C stringএবং ডেটা সবসময় কিছুটা বাফার হয় void *বা char *তাই আপনি চর অ্যারে পাস করতে পারেন। আপনি একই redisবা খুঁজে পেতে পারেন memcached। এক পর্যায়ে আমি std::stringকীর জন্য চর অ্যারে ব্যবহার করার বা স্থির করার সিদ্ধান্ত নিতে পারি তবে আন্ডারলাইন করে এটি সি স্ট্রিং হবে।
নিক

6
4 টি মন্তব্য যুক্ত করার পরিবর্তে, আপনার নিজের প্রশ্নটি সম্পাদনা করা
বাসাইল স্টারিঙ্কেভিচ

উত্তর:


3

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

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

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

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

আপনি বলছেন যে আপনি এর চেয়ে ভাল করার আশা করছেন map; দুটি জিনিস যা সম্পর্কে বলা যেতে পারে:

ক) আপনি সম্ভবত না;

খ) যে কোনও মূল্যে অকালে অপ্টিমাইজেশন এড়াতে হবে।

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

সি ++ তে মেমরি পরিচালনা সম্পর্কে বিভিন্ন উপায় রয়েছে এবং newঅপারেটরটিকে ওভারলোড করার ক্ষমতাটি কাজে লাগতে পারে। আপনার প্রকল্পের জন্য একটি সরল মেমরির বরাদ্দকারী বাইটগুলির একটি বিশাল অ্যারে প্রিলোকলেট করে এবং এটি একটি গাদা হিসাবে ব্যবহার করবে। ( byte* heap।) আপনার firstFreeByteশূন্যের আদিতে সূচক হবে, যা স্তূপে প্রথম বিনামূল্যে বাইট নির্দেশ করে। যখন Nবাইটগুলির জন্য একটি অনুরোধ আসে, আপনি ঠিকানাটি ফিরিয়ে দেন heap + firstFreeByteএবং এতে যুক্ত Nহন firstFreeByte। সুতরাং, মেমরির বরাদ্দ এত দ্রুত এবং দক্ষ হয়ে ওঠে যে এটি কার্যত কোনও সমস্যা হয়ে ওঠে।

অবশ্যই, আপনার সমস্ত স্মৃতি পূর্বনির্ধারণ করা ভাল ধারণা নাও হতে পারে, তাই আপনাকে চাহিদা অনুসারে বরাদ্দকৃত ব্যাংকগুলিতে আপনার স্তূপটি ভেঙে দিতে হবে এবং যে কোনও মুহুর্ত-নতুন ব্যাংক থেকে বরাদ্দের অনুরোধগুলি সরবরাহ করতে হবে keep

যেহেতু আপনার ডেটা অপরিবর্তনীয়, এটি একটি ভাল সমাধান। এটি আপনাকে পরিবর্তনশীল দৈর্ঘ্যের অবজেক্টগুলির ধারণাটি ত্যাগ করতে এবং প্রতিটিকে Pairতার ডেটাতে একটি পয়েন্টার রাখার অনুমতি দেয়, যেহেতু ডেটার জন্য অতিরিক্ত মেমরির বরাদ্দ কার্যত কিছুই হয় না।

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

তবে আপনি যদি প্রথমে আপনার ডাটাবেসের একটি সরল, খালি-ন্যূনতম, কার্যকারী সংস্করণ তৈরি না করেন এবং এটি সত্যিকারের অ্যাপ্লিকেশনে ব্যবহারের জন্য রাখেন না তবে এগুলি অবশ্যই অবাস্তব।

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