সি ++ এ অপ্রয়োজনীয় স্ট্রিং বরাদ্দ অনুকূলিতকরণ


10

আমার একটি মোটামুটি জটিল সি ++ উপাদান রয়েছে যার অভিনয়টি সমস্যা হয়ে দাঁড়িয়েছে become প্রোফাইলিং দেখায় যে মৃত্যুদণ্ড কার্যকর করার বেশিরভাগ সময় কেবলমাত্র মেমরির জন্য বরাদ্দ ব্যয় করে std::string

আমি জানি যে এই স্ট্রিংগুলির মধ্যে প্রচুর অপ্রয়োজনীয়তা রয়েছে। মুষ্টিমেয় মানগুলি খুব ঘন ঘন পুনরাবৃত্তি করে তবে প্রচুর অনন্য মানও রয়েছে। স্ট্রিংগুলি সাধারণত মোটামুটি ছোট।

আমি এখনই ভাবছি যদি এই ঘন ঘন বরাদ্দগুলি কোনওভাবে পুনরায় ব্যবহার করা বোধগম্য হয়। 1000 স্বতন্ত্র "ফুবার" মানগুলিতে 1000 পয়েন্টার পরিবর্তে, আমার একটি "ফুবার" মানের 1000 পয়েন্টার থাকতে পারে। এটি আরও মেমরির দক্ষ হবে এই বিষয়টি একটি দুর্দান্ত বোনাস তবে আমি বেশিরভাগ ক্ষেত্রেই এখানে বিলম্বিতা নিয়ে উদ্বিগ্ন।

আমি অনুমান করি যে একটি বিকল্প হ'ল ইতিমধ্যে বরাদ্দকৃত মানগুলির এক ধরণের একটি রেজিস্ট্রি বজায় রাখা তবে কি অপ্রয়োজনীয় মেমরির বরাদ্দের চেয়ে রেজিস্ট্রি লুকআপগুলি আরও দ্রুত করা সম্ভব? এটি কি একটি কার্যকর পন্থা?


6
সম্ভাব্য? হ্যাঁ অবশ্যই - অন্যান্য ভাষা এটি নিয়মিত করে (যেমন জাভা - স্ট্রিং ইন্টার্নিংয়ের সন্ধান)। তবে একটি গুরুত্বপূর্ণ বিষয় বিবেচনা করতে হবে তা হ'ল ক্যাশেযুক্ত অবজেক্টগুলিকে অপরিবর্তনীয় হওয়া দরকার যা স্ট্যান্ড :: স্ট্রিং নয়।
হাল্ক

2
এই প্রশ্নের আরো প্রাসঙ্গিক হল: stackoverflow.com/q/26130941
rwong

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

2
আপনি এই স্ট্রিং দিয়ে কি করছেন? এগুলি কি কেবল কোনও ধরণের সনাক্তকারী বা কী হিসাবে ব্যবহৃত হয়? বা তারা কিছু আউটপুট তৈরি করতে মিলিত হয়? যদি তা হয় তবে আপনি স্ট্রিং কনটেক্সটেশনগুলি কীভাবে করবেন? সঙ্গে +অপারেটর বা স্ট্রিং স্ট্রিম সঙ্গে? স্ট্রিংগুলি কোথা থেকে আসে? আপনার কোড বা বাহ্যিক ইনপুটটিতে লিটারেল?
আমন

উত্তর:


3

বেসিলের পরামর্শ অনুসারে আমি অভ্যন্তরীণ স্ট্রিংগুলিতে প্রচুর ঝোঁক রেখেছি, যেখানে স্ট্রিং লুপ সংরক্ষণ এবং তুলনা করতে 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;
}

2

আপনি কিছু অভ্যন্তরীণ স্ট্রিং যন্ত্রপাতি রাখতে চাইবেন (তবে স্ট্রিংগুলি পরিবর্তনযোগ্য হওয়া উচিত, সুতরাং ব্যবহার করুন const std::string)। আপনি কিছু প্রতীক চাইবেন । আপনি স্মার্ট পয়েন্টারগুলিতে সন্ধান করতে পারেন (উদাঃ এসটিডি :: শেয়ার্ড_পিটার )। অথবা এমনকি স্ট্রিং :: স্ট্রিং_ভিউ সি ++ 17 এ।


0

সংকলক নির্মাণের সময় আমরা ডেটা চেয়ার নামে কিছু ব্যবহার করতাম (ডেটা-ব্যাঙ্কের পরিবর্তে, ডিবি-র জন্য একটি চলাচলকারী জার্মান অনুবাদ)। এটি কেবল একটি স্ট্রিংয়ের জন্য একটি হ্যাশ তৈরি করেছে এবং বরাদ্দের জন্য এটি ব্যবহৃত হয়েছিল। সুতরাং কোনও স্ট্রিং হিপ / স্ট্যাকের কোনও মেমরির টুকরা ছিল না তবে এই ডেটা-চেয়ারে একটি হ্যাশ কোড ছিল। আপনি Stringযেমন একটি বর্গ দ্বারা প্রতিস্থাপন করতে পারে । যদিও বেশ কয়েকটি কোড পুনর্নির্মাণের প্রয়োজন। এবং অবশ্যই এটি কেবল r / o স্ট্রিংয়ের জন্য ব্যবহারযোগ্য।


অনুলিপি সম্পর্কে কি। আপনি যদি স্ট্রিংটি পরিবর্তন করেন তবে আপনি হ্যাশটিকে পুনরায় গণনা করে পুনরুদ্ধার করবেন। বা যে কাজ করবে না?
জেরি যেরেমিয়া

@ জেরি জেরেমিয়া এটি আপনার আবেদনের উপর নির্ভর করে। আপনি হ্যাশ দ্বারা প্রতিনিধিত্ব করা স্ট্রিংটি পরিবর্তন করতে পারেন এবং আপনি যখন হ্যাশের উপস্থাপনাটি পুনরুদ্ধার করবেন তখন আপনি নতুন মান পাবেন। সংকলক প্রসঙ্গে আপনি একটি নতুন স্ট্রিংয়ের জন্য একটি নতুন হ্যাশ তৈরি করবেন।
qwerty_so

0

লক্ষ্য করুন যে মেমরি বরাদ্দ এবং প্রকৃত মেমরি উভয়ই দুর্বল পারফরম্যান্সের সাথে সম্পর্কিত te

আসলে মেমরি বরাদ্দ করার খরচটি অবশ্যই খুব বেশি। সুতরাং স্টাড :: স্ট্রিংটি ইতিমধ্যে ছোট স্ট্রিংয়ের জন্য জায়গার বরাদ্দ ব্যবহার করতে পারে এবং প্রকৃত বরাদ্দের পরিমাণ অতএব আপনি প্রথম অনুমানের চেয়ে কম হতে পারে। যদি এই বাফারটির আকারটি যথেষ্ট পরিমাণে বড় না হয় তবে আপনি উদাহরণস্বরূপ ফেসবুকের স্ট্রিং ক্লাস ( https://github.com/facebook/folly/blob/master/folly/FBString.h ) দ্বারা অনুপ্রাণিত হয়ে যা 23 টি অক্ষর ব্যবহার করে বরাদ্দ দেওয়ার আগে অভ্যন্তরীণভাবে

খরচ ব্যবহার মেমরি অনেক এরও কোন মূল্য নেই মূল্য। এটি সম্ভবত সবচেয়ে বড় অপরাধী: আপনার মেশিনে প্রচুর পরিমাণে র‌্যাম থাকতে পারে, তবে, ক্যাশে আকারগুলি এখনও যথেষ্ট ছোট যে ইতিমধ্যে ক্যাশেড নয় এমন মেমরি অ্যাক্সেস করার সময় এটির কার্যকারিতা ক্ষতিগ্রস্থ করবে। আপনি এই সম্পর্কে এখানে পড়তে পারেন: https://en.wikedia.org/wiki/Locality_of_references


0

স্ট্রিং অপারেশনগুলি দ্রুত করার পরিবর্তে, আরেকটি পদ্ধতি হ'ল স্ট্রিং অপারেশনগুলির সংখ্যা হ্রাস করা। উদাহরণস্বরূপ, একটি এনাম দিয়ে স্ট্রিংগুলি প্রতিস্থাপন করা সম্ভব হবে?

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

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