বেসিলের পরামর্শ অনুসারে আমি অভ্যন্তরীণ স্ট্রিংগুলিতে প্রচুর ঝোঁক রেখেছি, যেখানে স্ট্রিং লুপ সংরক্ষণ এবং তুলনা করতে 32-বিট সূচককে অনুবাদ করে। এটি আমার ক্ষেত্রে কার্যকর যেহেতু আমার মাঝে মাঝে "x" নামে একটি সম্পত্তি সহ কয়েক হাজার থেকে কয়েক মিলিয়ন উপাদান থাকে, যেমন, এটি এখনও ব্যবহারকারী-বান্ধব স্ট্রিং নাম হওয়া দরকার কারণ এটি প্রায়শই নাম দ্বারা স্ক্রিপ্ট দ্বারা অ্যাক্সেস করা হয়।
আমি দেখার জন্য একটি ট্রাই ব্যবহার করেছি (এর সাথে পরীক্ষাও করেছি unordered_map
তবে আমার সুরযুক্ত ট্রাই মেমরি পুলের সাহায্যে কমপক্ষে আরও ভাল পারফরম্যান্স শুরু করেছে এবং কাঠামোটি অ্যাক্সেস করার সময় কেবল লক না করেই থ্রেড-নিরাপদ করা আরও সহজ ছিল) তবে এটি তেমনটি নয় নির্মাণ হিসাবে নির্মাণের জন্য দ্রুত std::string
। পয়েন্টটি স্ট্রিং ইক্যুয়ালিটি যাচাইয়ের মতো পরবর্তী ক্রিয়াকলাপগুলিকে আরও গতিযুক্ত করার জন্য যা আমার ক্ষেত্রে কেবল সাম্যতার জন্য দুটি পূর্ণসংখ্যা পরীক্ষা করার জন্য এবং মেমরির ব্যবহারকে হ্রাস করার জন্য উত্সাহিত হয়।
আমি অনুমান করি যে একটি বিকল্প হ'ল ইতিমধ্যে বরাদ্দকৃত মানগুলির এক ধরণের একটি রেজিস্ট্রি বজায় রাখা তবে কি অপ্রয়োজনীয় মেমরির বরাদ্দের চেয়ে রেজিস্ট্রি লুকআপগুলি আরও দ্রুত করা সম্ভব?
এটি একটি কাঠামোর চেয়ে অনেক দ্রুত ডেটা কাঠামোর মাধ্যমে অনুসন্ধান করা শক্ত হতে চলেছে malloc
উদাহরণস্বরূপ, যদি আপনার কোনও ক্ষেত্রে যদি আপনি কোনও ফাইলের মতো বাহ্যিক ইনপুট থেকে স্ট্রিংয়ের নৌকা বোঝা পড়ছেন, তবে আমার প্রলোভনটি সম্ভব হলে অনুক্রমিক বরাদ্দ ব্যবহার করা হবে। এটি নেতিবাচক সাথে আসে যা আপনি পৃথক স্ট্রিংয়ের মেমরি মুক্ত করতে পারবেন না। বরাদ্দকারীর দ্বারা চালিত সমস্ত স্মৃতি একবারে মুক্তি দিতে হবে বা একদম নয়। তবে অনুক্রমিক বরাদ্দকারী এমন ক্ষেত্রে কার্যকর হতে পারে যেখানে আপনাকে কেবল একটি সরল ক্রমবর্ধমান ফ্যাশনে মেমরির ক্ষুদ্র আকারের পরিবর্তনশীল-আকারের অংশগুলির একটি বোটোড বরাদ্দ করতে হবে, কেবলমাত্র পরে এটি সমস্ত পরে টস করতে হবে। আমি জানি না এটি আপনার ক্ষেত্রে প্রযোজ্য কিনা বা না, তবে প্রয়োগের সময়, ঘন ঘন কিশোর মেমরির বরাদ্দ সম্পর্কিত হটস্পট ঠিক করার একটি সহজ উপায় হতে পারে (যা অন্তর্নিহিতের চেয়ে ক্যাশে মিস এবং পৃষ্ঠা ত্রুটিগুলির সাথে আরও বেশি কিছু করতে পারে) এলগোরিদম দ্বারা ব্যবহৃত, বলুন, malloc
)।
স্থির আকারের বরাদ্দগুলি ক্রমবর্ধমান বরাদ্দকারী সীমাবদ্ধতাগুলি ছাড়াই গতি বাড়ানো সহজ যা আপনাকে পরে মেমরির নির্দিষ্ট অংশগুলি পুনরায় ব্যবহার করতে বাধা দেয়। কিন্তু ডিফল্ট বরাদ্দকারীর তুলনায় ভেরিয়েবল-আকারের বরাদ্দ দ্রুত তৈরি করা বেশ শক্ত। মূলত আপনি যেকোন ধরণের মেমরির বরাদ্দকারী এর চেয়ে দ্রুতগতিতে তৈরি malloc
করা সাধারণভাবে অত্যন্ত শক্ত তবে যদি আপনি তার প্রয়োগযোগ্যতা সংকীর্ণ সীমাবদ্ধতাগুলি প্রয়োগ না করেন। এর একটি সমাধান হ'ল একটি নির্দিষ্ট আকারের বরাদ্দকারীকে ব্যবহারের জন্য বলুন, সমস্ত স্ট্রিং যা 8 বাইট বা তার চেয়ে কম যদি আপনার একটি বোটলোড থাকে এবং এর চেয়ে বেশি স্ট্রিং বিরল ক্ষেত্রে হয় (যার জন্য আপনি কেবলমাত্র ডিফল্ট বরাদ্দ ব্যবহার করতে পারেন)। এর অর্থ হ'ল 1-বাইট স্ট্রিংয়ের জন্য 7 বাইট নষ্ট হয় তবে এটি বরাদ্দ-সম্পর্কিত হটস্পটগুলি অপসারণ করা উচিত, যদি বলি, 95% সময় আপনার স্ট্রিংগুলি খুব সংক্ষিপ্ত হয়।
আমার কাছে সবেমাত্র ঘটে যাওয়া আরেকটি সমাধান হ'ল অনিবন্ধিত লিঙ্কযুক্ত তালিকাগুলি ব্যবহার করা যা পাগল মনে হতে পারে তবে আমাকে শুনবে।
এখানে ধারণাটি হ'ল প্রতিটি অনিবন্ধিত নোডকে ভেরিয়েবল-আকারের পরিবর্তে একটি নির্দিষ্ট আকারের করা। যখন আপনি এটি করেন, আপনি একটি অত্যন্ত দ্রুত স্থির আকারের কাণ্ড বরাদ্দকারী ব্যবহার করতে পারেন যা মেমরিকে পুল করে, পরিবর্তনশীল-আকারের স্ট্রিংগুলিকে একসাথে যুক্ত করার জন্য স্থির আকারের অংশগুলি বরাদ্দ করে। এটি মেমোরির ব্যবহার হ্রাস করবে না, লিঙ্কগুলির ব্যয়ের কারণে এটি এতে যুক্ত হবে tend তবে আপনার প্রয়োজনের জন্য উপযুক্ত ভারসাম্য খুঁজে পেতে আপনি অনিয়ন্ত্রিত আকার নিয়ে খেলতে পারেন। এটি এক ধরণের উদ্ভট ধারণা কিন্তু মেমরি সম্পর্কিত হটস্পটগুলি অপসারণ করা উচিত যেহেতু আপনি এখন শক্তিশালী সংলগ্ন ব্লকে ইতিমধ্যে বরাদ্দ থাকা মেমরি কার্যকরভাবে কার্যকর করতে পারেন এবং স্বতন্ত্রভাবে স্ট্রিংগুলি মুক্ত করার সুবিধা পেতে পারেন। এখানে আমি একটি সরল অল 'স্থির বরাদ্দকারী লিখেছি (আমি অন্য কারও জন্য তৈরি চিত্রাঙ্কিত, উত্পাদন-সম্পর্কিত ফ্লাফ ছাড়াই) যা আপনি নির্দ্বিধায় ব্যবহার করতে পারেন:
#ifndef FIXED_ALLOCATOR_HPP
#define FIXED_ALLOCATOR_HPP
class FixedAllocator
{
public:
/// Creates a fixed allocator with the specified type and block size.
explicit FixedAllocator(int type_size, int block_size = 2048);
/// Destroys the allocator.
~FixedAllocator();
/// @return A pointer to a newly allocated chunk.
void* allocate();
/// Frees the specified chunk.
void deallocate(void* mem);
private:
struct Block;
struct FreeElement;
FreeElement* free_element;
Block* head;
int type_size;
int num_block_elements;
};
#endif
#include "FixedAllocator.hpp"
#include <cstdlib>
struct FixedAllocator::FreeElement
{
FreeElement* next_element;
};
struct FixedAllocator::Block
{
Block* next;
char* mem;
};
FixedAllocator::FixedAllocator(int type_size, int block_size): free_element(0), head(0)
{
type_size = type_size > sizeof(FreeElement) ? type_size: sizeof(FreeElement);
num_block_elements = block_size / type_size;
if (num_block_elements == 0)
num_block_elements = 1;
}
FixedAllocator::~FixedAllocator()
{
// Free each block in the list, popping a block until the stack is empty.
while (head)
{
Block* block = head;
head = head->next;
free(block->mem);
free(block);
}
free_element = 0;
}
void* FixedAllocator::allocate()
{
// Common case: just pop free element and return.
if (free_element)
{
void* mem = free_element;
free_element = free_element->next_element;
return mem;
}
// Rare case when we're out of free elements.
// Create new block.
Block* new_block = static_cast<Block*>(malloc(sizeof(Block)));
new_block->mem = malloc(type_size * num_block_elements);
new_block->next = head;
head = new_block;
// Push all but one of the new block's elements to the free stack.
char* mem = new_block->mem;
for (int j=1; j < num_block_elements; ++j)
{
void* ptr = mem + j*type_size;
FreeElement* element = static_cast<FreeElement*>(ptr);
element->next_element = free_element;
free_element = element;
}
return mem;
}
void FixedAllocator::deallocate(void* mem)
{
// Just push a free element to the stack.
FreeElement* element = static_cast<FreeElement*>(mem);
element->next_element = free_element;
free_element = element;
}