AMcoder - javascript, python, java, html, php, sql

move boost::interprocess::string ընդհանուր հիշողության մեջ

Ես ուզում էի ներդնել հաղորդագրությունների որոշակի հերթ (վեկտորի վրա հիմնված) ցանցից որևէ կերպ տվյալների մշակման համար, և դա անելու համար օգտագործեցի ընդհանուր հիշողություն հաղորդագրությունները պահելու համար և հանդիպեցի դրա հետ կապված խնդրին, բանն այն է, որ իմ կոդը աշխատում է լավ, երբ ես առաջին անգամ գործարկում եմ այն, երբ ուզում եմ նորից գործարկել, ես ստանում եմ segfaut, երբ ուզում եմ նոր արժեք վերագրել իմ հերթում գտնվող տողին ընդհանուր հիշողության մեջ, իրականում իմ դեպքում, երբ ուզում եմ այն ​​տեղափոխել (նույն խնդիրը գոյություն ունի, երբ ես ուզում եմ պատճենել այն): Խնդիրը գոյություն չունի, երբ SSO-ն աշխատում է, այնպես որ, երբ ես բավականաչափ փոքր տող ունեմ: Ինչ եմ սխալվել:

#include <atomic>
#include <exception>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>

namespace bip = boost::interprocess;

struct BadSharedMemoryAccess final : public std::exception
{
    BadSharedMemoryAccess(std::string&& msg):
        msg_{std::move(msg)}
{}

virtual const char* what() const noexcept
{
    return msg_.c_str();
}

private:
    std::string msg_;
};

struct Message
{
    bip::string message_;
};

template<typename Alloc>
class MyCustomData final
{
public:
    using allocator_type = typename Alloc::template rebind<Message>::other;

    MyCustomData(std::size_t number_of_messages, Alloc alloc = {}) :
        init_add_index_{0},
        init_handle_index_{-1},
        messages_{number_of_messages, alloc}
    {}

public:
    uint_fast64_t init_add_index_;
    int_fast64_t init_handle_index_;
    std::vector<Message, Alloc> messages_;
//    bip::vector<data::Message, Alloc> messages_;
};

template<typename DataType, typename DataAllocator>
class SharedMemory
{
public:
    template<typename... Args>
    SharedMemory(std::string const& shm_segment_name, std::size_t const segment_size,
        std::string const& shm_object_name, Args&&... args) :
            shm_object_name_{shm_object_name}
    {
        std::cout << "attempt to allocate space for shared memory segment " << shm_segment_name
              << ", size: ." << segment_size << std::endl;
        setSharedMemorySize(shm_segment_name, segment_size);

        DataAllocator const allocInstance{shm_.get_segment_manager()};
        data_ = shm_.find_or_construct<DataType>(shm_object_name.c_str())(std::forward<Args>(args)..., allocInstance);
        if (data_)
            std::cout << "shared memory segment has been allocated" << std::endl;
        else
            std::cout << "shared memory has not been constructed or founded" << std::endl;
    }

    virtual ~SharedMemory()
    {
        std::cout << "shared memory segment will be closed." << std::endl;
    }

    void setSharedMemorySize(std::string const& shm_segment_name, std::size_t const segment_size)
    {
        auto page_size = bip::mapped_region::get_page_size();
        auto const page_increase_rate{2};
        while (page_size < segment_size)
        {
            page_size *= page_increase_rate;
        }

        std::cout <<"seting page size: " << page_size << std::endl;
        shm_ = bip::managed_shared_memory{bip::open_or_create, shm_segment_name.c_str(), page_size};
        std::cout << "space for shared memory has been successfully allocated." << std::endl;
    }

    DataType& getData()
    {
        if (not data_)
            throw BadSharedMemoryAccess{"cannot access " + shm_object_name_};
        return *data_;
    }

protected:
    DataType* data_;

private:
    std::string const shm_object_name_;
    bip::managed_shared_memory shm_;
};

namespace sharable
{
    using DataAllocator = bip::allocator<Message, bip::managed_shared_memory::segment_manager>;
    template<typename Alloc>
    using DataType = MyCustomData<Alloc>;
}

int main()
{
    std::size_t const max_number_of_elements_in_container{1000000};
    auto shmem_data = std::make_shared<SharedMemory<MyCustomData<sharable::DataAllocator>, sharable::DataAllocator>>(
        "SHM_SEGMENT", sizeof(MyCustomData<sharable::DataAllocator>) +
            (max_number_of_elements_in_container * sizeof(Message) * 2),
        "SHM_CONTAINER", max_number_of_elements_in_container);

    std::vector<bip::string> feed{max_number_of_elements_in_container};
    for (std::size_t i = 0; i < max_number_of_elements_in_container; ++i)
    {
        std::string s{"blablabla11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" + std::to_string(i)};
        feed[i] = s.c_str();
    }

    auto& data = shmem_data->getData();
    auto& shmem_vec = data.messages_;
    std::cout << "addr: " << shmem_vec.data() << std::endl;
    for (std::size_t i = 0; i < max_number_of_elements_in_container; ++i)
    {
//        if (i == 0)
//            std::cout << "msg: " << shmem_vec[i].message_ << std::endl;
        auto msg = feed[i];
        shmem_vec[i].message_ = std::move(msg);
    }
    return 0;
}

Պատասխանները:


1
  1. Դուք չեք օգտագործում ընդհանուր հիշողության հատկացուցիչ տողերի համար: Այդ առումով, ձեր հարցը նույնն է, ինչ circular_buffer and manager_mapped_file հատվածավորման սխալ: Դուք կարող եք կարդալ դա ընդհանուր ներածության համար:

  2. Ձեր օրինակը բարդացնում է իրավիճակը՝ փաթաթելով տողերը ձեր սեփական կառուցվածքների մեջ: Դա նշանակում է, որ դուք ստանում եք շատ հոգնեցուցիչ աշխատանք, որը անցնում է բաշխիչների շուրջը: Uses_allocator մոտեցման համար, որը scoped_allocator_adaptor-ի հետ միասին կարող է մեղմացնել այդ ցավը, տես, օրինակ. boost::interprocess-ի չհամօգտագործվող պատճենների ստեղծում ընդհանուր հիշողության օբյեկտներ:

  3. Կարդալով ձեր ծածկագրի մնացած մասը, ես մի փոքր շփոթված եմ: Ինչո՞ւ կաղապարեք ձեր SharedMemory տիպը հատկացնողով: Ես նկատի ունեմ, որ SharedMemory-ը պետք է լինի մեկ կետը, որը պատասխանատու է ճիշտ բաշխիչ ընտրելու և անցնելու համար, այնպես չէ՞: Ինչպե՞ս կարող էր այն աշխատել արտաքին տրամադրված հատկացնողի հետ:

  4. Կան typedef-ներ, որոնք չօգտագործված են, դուք նոր հատված եք կազմում յուրաքանչյուր օբյեկտի համար, թեև այն կարող է լինել նույն ընդհանուր հիշողությունից (միևնույն էջերը մի քանի անգամ քարտեզագրելով հիշողության մեջ): Այնուամենայնիվ, դուք ինչ-որ կերպ կարծում եք, որ կարևոր է կիսել սեփականության իրավունքը նման օրինակի վրա (make_shared):

  5. Չափի հաշվարկները պարզապես սխալ են. դրանք հաշվի են առնում միայն ձեր Message կառուցվածքի չափը, ոչ թե հատկացված տողային տվյալները: Դուք կարծես մոռացել եք, որ քարտեզագրված հիշողությունը նույնպես վիրտուալ հիշողություն է: Հիմքում ընկած պահեստը կկարողանա սակավ հատկացնել: Այսպիսով, ինչո՞ւ չպահպանել մեծ քանակությամբ հիշողություն և պարզապես պատասխանել, երբ սպառվի:

  6. Դուք խոսում և կոդավորում եք (որոշ) շարժման իմաստաբանությունը, բայց հետո գրում եք.

    for (std::size_t i = 0; i < max_number_of_elements_in_container; ++i) {
        auto msg = feed[i];
        shmem_vec[i].message_ = std::move(msg);
    }
    

    Դա շփոթված է: Ինչ լավ է քայլը (եթե այն աշխատեց, տե՛ս ստորև), եթե առաջին հերթին, այնուամենայնիվ, բացահայտ պատճենեք.

        auto msg = feed[i];
    
  7. Սրանք մտահոգիչ նշաններ են.

    uint_fast64_t init_add_index_;
    int_fast64_t  init_handle_index_;
    

    Կարծես թե դուք պլանավորում եք դրանք միաժամանակ օգտագործել բազմաթիվ գործընթացներից/թելերից²: Նման դեպքում դուք պետք է ավելացնեք համաժամացում կամ օգտագործեք առնվազն atomic<> տեսակներ:

Ամփոփելով ինձ թվում է, որ դուք կարող եք այնքան շատ եք փորձում թաքցնել բարդությունը, որ պատահաբար ավելացրել եք այն:

Շարժվելու մասին

Դուք հարցնում եք ընդհանուր հիշողության մեջ ընդհանուր տողը տեղափոխելու մասին: Հարցի այս մասի համար ենթադրենք, որ իրականում ձեր տողերը տեղաբաշխված են եղել ընդհանուր հիշողության մեջ:

Տեսնելով, թե ինչպես են աշխատում շարժվող տողերը, դժվար չէ տեսնել, որ շարժվող տողերը ընդհանուր հիշողության մեջ կաշխատեն ճիշտ այնպես, ինչպես կաշխատեն դրանք կույտի ներսում տեղափոխելը. օբյեկտի հասցեն տարբեր կլինի, բայց հատկացված հիշողության ներքին ցուցիչը նույնը կլինի:

Այնուամենայնիվ, կոդը այլ բան է անում. այն չի տեղափոխվում համօգտագործվող հիշողության մեջ: Այն փորձում է տեղափոխել կույտից ընդհանուր հիշողություն: Սա ակնհայտորեն անվտանգ չի լինի, քանի որ ընդհանուր հիշողության օբյեկտները չեն կարող օգտակար մատնանշել ընդհանուր հիշողության հատվածից դուրս որևէ բան (ցանկացած այլ գործընթաց կառաջարկի չսահմանված վարքագիծ, որն անուղղակի է նման ցուցիչի միջոցով): .

Ինչպես հաճախ, C++-ում դուք մասամբ ձերն եք՝ նման վթարները կանխելու համար՝ C++11 basic_string<>::swap նշում է.

Վարքագիծը որոշված ​​չէ, եթե Allocator-ը չի տարածվում swap-ում, և *this-ի և other-ի բաշխիչները անհավասար են:

Move-constructor նշված է, որ ունի բարդություն.

մշտական. Եթե ​​տրված է alloc և alloc != other.get_allocator(), ապա գծային

Նկատի ունեցեք, որ բաշխիչների իմաստաբանությունը բեռնարկղերը պատճենելիս/տեղափոխելիս (basic_string<>-ը կոնտեյներ է, որը նման է std::vector<>-ին) ավելի շատ է ներգրավված.

մուտքագրեք պատկերի նկարագրությունը այստեղ

Ինչ անել?

Ընդհանուր առմամբ, եթե ձեր բախտը բերել է, քայլը չի ​​կազմվի, քանի որ հատկացուցիչները անհամատեղելի տիպի են և ոչ մեկը չի մատակարարվում (օրինակ՝ use_allocator արձանագրությամբ):

Եթե ​​դուք ավելի քիչ հաջողակ եք, այն կկազմվի, բայց այն (բարեբախտաբար) չի կատարի այդ քայլը, քանի որ հայտնաբերում է, որ հատկացնողները հավասար չեն, և հետևաբար, այն վերադառնում է պահեստի պատճենմանը:

Եթե ​​դուք բացարձակապես անհաջող եք, դուք ընտրել եք այնպիսի կոնֆիգուրացիա, որտեղ տեսակները համատեղելի են, և բաշխիչները կազմաձևված չեն, որպեսզի ապահով կերպով տարածվեն բեռնարկղերի տեղափոխման/պատճենման ժամանակ, կամ մեկ այլ հանգամանք հանգեցնում է նրան, որ հատկացուցիչները չեն կարողանում հայտնաբերել անհամատեղելիությունը¹, և դուք հայտնվում եք UB-ի հետ:

Այս դեպքում շատ ավելի հեշտ տարբերակ կա՝ դուք գիտեք, որ չեք կարող շարժվել: Հետևաբար, մի պահանջեք տեղափոխություն:

Ռիսկը կանխվեց.

Մեր վերքերը բուժելու որոշ ծածկագիր

Կոդի և հարցի բարդությունը խախտելուց հետո, եկեք կառուցողական լինենք և ցույց տանք, թե ինչ կարող ենք անել՝ խնդիրները շտկելու համար.

#include <exception>
#include <iomanip>
#include <iostream>
#include <random>

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>

namespace bip = boost::interprocess;

struct BadSharedMemoryAccess final : std::runtime_error {
    BadSharedMemoryAccess(std::string msg) : std::runtime_error{ std::move(msg) } {}
};

Դա նախերգանքն է: Հիմա եկեք հայտնենք մեր մտադրությունները.

using Segment = bip::managed_shared_memory;
template <typename U> using Alloc = bip::allocator<U, Segment::segment_manager>;

Սա հեշտացնում է սեգմենտին և դրա հատկացնողներին հղում կատարելը (և գուցե անջատել):

using Message       = bip::string;
using Feed          = bip::vector<Message>;
using SharedMessage = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;
using SharedFeed    = bip::vector<SharedMessage, Alloc<SharedMessage> >;

Պարզապես սահմանեք մեր տիրույթի սուբյեկտները: Օգտագործելով bip::string/bip::vector-ը կույտային և ընդհանուր տեղաբաշխման տարբերակների համար՝ մենք ստանում ենք լավագույն փոխազդեցությունը երկուսի միջև;

class MyCustomData final {
  public:
    using allocator_type = SharedFeed::allocator_type;

    MyCustomData(std::size_t capacity, allocator_type alloc)
        : messages_(capacity, SharedMessage(alloc), alloc) // don't brace initlaize
    { }

    auto&       messages()       { return messages_; }
    auto const& messages() const { return messages_; }

  private:
    uint_fast64_t init_add_index_ = 0;
    int_fast64_t  init_handle_index_ = -1;
    SharedFeed messages_;
};

Առայժմ հանվել է virtual կործանիչը և Message կառուցվածքը, որը հարմարության համար պարզապես փաթաթել է bip::string:

template <typename T> class SharedMemory final {
  public:
    template <typename... Args>
    SharedMemory(std::string const& shm_segment_name,
                 std::size_t const segment_size,
                 std::string const& shm_object_name,
                 Args&&... args)
        : shm_ { bip::open_or_create, shm_segment_name.c_str(), segment_size }
    {
        data_ = shm_.find_or_construct<T>
            (shm_object_name.c_str())
            (std::forward<Args>(args)...,
             shm_.get_segment_manager())
            ;

        if (!data_) throw BadSharedMemoryAccess {"cannot access " + shm_segment_name + "/" + shm_object_name};
    }

    T const& get() const { return *data_; }
    T&       get()       { return *data_; }

    auto free() const { return shm_.get_free_memory(); }
  protected:
    T* data_;

  private:
    Segment shm_;
};

Ինձ զարմացնում է, որ SharedMemory-ը չափազանց շատ պարտականություններ ունի. մի կողմից այն փորձում է լինել խելացի հղում ընդհանուր օբյեկտների համար, իսկ մյուս կողմից՝ կառավարում է հատվածը: Սա հանգեցնում է խնդիրների, եթե դուք իրականում ցանկանում եք մի հատվածում ունենալ բազմաթիվ օբյեկտներ: Մտածեք բաժանվել Shared::Segment-ի և Shared::Object<T>-ի:

Feed generate_heap_feed(size_t n) {
    Feed feed;
    feed.reserve(n);
    for (size_t i = 0; i < n ; ++i) {
        feed.emplace_back(
            "blablabla11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
            + std::to_string(i));
    }
    return feed;
}

Արդյունահանվել է փորձնական սնուցման գեներատորը main-ից:

int main() {
    static constexpr std::size_t capacity { 1000000 };
    static constexpr auto estimate = 300ull << 20; // 300 MiB (<< 10 kilo, << 20 mebi, << 30 gibi)

Սխալ հաշվարկները³ փոխարինեց առատաձեռն գնահատականով: Չափումները տես ստորև:

    using SharedData = SharedMemory<MyCustomData>;
    SharedData shmem_data("SHM_SEGMENT", estimate, "SHM_CONTAINER", capacity);
    std::cout << "Free: " << shmem_data.free() << "\n";

Գեղեցիկ և ընթեռնելի: Իմ համակարգում տպում է "Free: 282572448" առաջին գործարկման ժամանակ:

    Feed const feed      = generate_heap_feed(capacity);
    SharedFeed& shm_feed = shmem_data.get().messages();

Այժմ մենք ունենք մեր հոսքերը կողք կողքի, եկեք պատճենենք.

    // copy feed from heap to shm
    auto const n = std::min(feed.size(), shm_feed.size());
    std::copy_n(feed.begin(), n, shm_feed.begin());

    std::cout << "Copied: " << n << "\n";
    std::cout << "Free: " << shmem_data.free() << "\n";

Այսքանը: Մենք չենք փորձում շարժվել, քանի որ գիտենք, որ դա չի կարող աշխատել: bip::basic_string ճիշտ գիտի, թե ինչպես պատճենել անհամատեղելի բաշխիչների միջև: Ոչ մի քրտինք:

Լավ չափման համար եկեք տպենք որոշ ախտորոշիչ տեղեկատվություն.

    {
        // check some random samples
        std::default_random_engine prng{std::random_device{}()};
        auto pick = [&] { return std::uniform_int_distribution<>(0, n-1)(prng); };

        for (auto index : {pick(), pick(), pick(), pick()}) {
            std::string_view a = feed.at(index);
            std::string_view b = shm_feed.at(index);
            std::cout << "Message #" << index
                << (a == b? " OK":" FAIL")
                << " " << std::quoted(b) << std::endl;
        }
    }
}

Դիտեք այն Live on Coliru⁴

Տպումներ, օրինակ՝ մուտքագրեք պատկերի նկարագրությունը այստեղ

Հատկապես նշեք ֆայլի չափերը (--apparent-size ընդդեմ սկավառակի չափի): Սա հաստատում է իմ տեսակետը նոսր հատկացման մասին: Նույնիսկ եթե դուք վերապահել եք 100 ՏԲ, SHM_CONTAINER-ի արդյունավետ չափը դեռ կկազմի 182 ՄԲ:

ԲՈՆՈՒՍ ԲԱԺԻՆՆԵՐ

Scoped Allocator Adapters

Պարզապես մեկ տողի փոխարինում.

template <typename U> using Alloc = bip::allocator<U, Segment::segment_manager>;

հետ

template <typename U> using Alloc = boost::container::scoped_allocator_adaptor<
    bip::allocator<U, Segment::segment_manager> >;

անում է հնարքը՝ բացելով կախարդական հատկացնողի տարածումը, օրինակ. վեկտորից տող իր տարրերը կառուցելիս (emplace կամ assign-ով): Այսպիսով, մենք կարող ենք ավելի պարզեցնել copy_n-ը հետևյալից.

// copy feed from heap to shm
auto const n = std::min(feed.size(), shm_feed.size());
std::copy_n(feed.begin(), n, shm_feed.begin());

std::cout << "Copied: " << n << "\n";

պարզապես՝

shm_feed.assign(feed.begin(), feed.end());
std::cout << "Copied: " << shm_feed.size() << "\n";

Այն ունի ճիշտ նույն բաշխման վարքագիծը, ինչ նախկինում: Դիտեք այն նաև Ուղիղ եթեր Coliru-ում:

Պոլիմորֆ հատկացուցիչներ (c++17)

Սա սկզբունքորեն ոչինչ չի փոխի, բացառությամբ.

  • դա կստիպի Feed/SharedFeed-ը և Message/SharedMessage-ը կիսել նույն ստատիկ տիպը
  • այն կունենա scoped-allocator վարքագիծ, ինչպես նախկինում լռելյայն

Այնուամենայնիվ, քանի դեռ չենք ստացել համապատասխան աջակցություն ստանդարտի շքեղ ցուցիչների համար, սա երազանք է.

  • #P47#
    #P48#

Նորից դարձնո՞ւմ եք Message Struct:

Դե, Ավելի շատ նման է նորից պայքարին: Ես ընդունում եմ, որ ատում եմ գրել հատկացնող տվյալների տեսակները: Սա, անկասկած, օպտիմալ չէ, բայց դա նվազագույն բանն է, որը ես կարող էի անել, որպեսզի գործն աշխատի.

template <typename Alloc>
struct BasicMessage {
    // pre-c++17:
    //  using allocator_type = typename Alloc::template rebind<char>::other;
    using allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<char>;

    BasicMessage(std::allocator_arg_t, allocator_type alloc)
        : _msg(alloc) { }

    template <typename T1, typename... T,
             typename = std::enable_if_t<
                    not std::is_same_v<std::allocator_arg_t, std::decay_t<T1> >
                 >
        >
    explicit BasicMessage(T1&& a, T&&... init)
        : _msg(std::forward<T1>(a), std::forward<T>(init)...) { }

    template <typename OtherAlloc>
    BasicMessage(BasicMessage<OtherAlloc> const& other, allocator_type alloc)
        : _msg(other.message().begin(), other.message().end(), alloc) { }

    template <typename OtherAlloc, typename OM = BasicMessage<OtherAlloc> >
    std::enable_if_t<
        not std::is_same_v<allocator_type, typename OM::allocator_type>,
        BasicMessage&>
    operator=(BasicMessage<OtherAlloc> const& other) {
        _msg.assign(other.message().begin(), other.message().end());
        return *this;
    }

    template <typename OtherAlloc>
    BasicMessage(std::allocator_arg_t, allocator_type alloc, BasicMessage<OtherAlloc> const& other)
        : _msg(other.message().begin(), other.message().end(), alloc) { }

    BasicMessage(BasicMessage const&) = default;
    BasicMessage(BasicMessage&&) = default;
    BasicMessage& operator=(BasicMessage const&) = default;
    BasicMessage& operator=(BasicMessage&&) = default;

    auto& message() const { return _msg; }
    auto& message()       { return _msg; }
  private:
    bip::basic_string<char, std::char_traits<char>, allocator_type> _msg;
};

using Message       = BasicMessage<std::allocator<char> >;
using Feed          = bip::vector<Message>;
using SharedMessage = BasicMessage<Alloc<char> >;
using SharedFeed    = bip::vector<SharedMessage, Alloc<SharedMessage> >;

Լավ կողմն այն է, որ այն դեռ օգտագործում է կախարդական հանձնարարությունը վերը ներկայացված scoped_allocator_adaptor ուղղման շնորհիվ: Թերևս, եթե դա ցանկալի չլիներ, դուք կարող եք մի փոքր ավելի քիչ բարդությունից խուսափել:

Այլուր ինտերֆեյսի աննշան փոփոխություններով.

: messages_(capacity, SharedMessage(std::allocator_arg, alloc), alloc) // don't brace initlaize

և

    std::string_view a = feed.at(index).message();
    std::string_view b = shm_feed.at(index).message();

ամեն ինչ դեռ աշխատում է, տես Live On Coliru


¹ ոչ ստանդարտ, հետևաբար վախեցնելու մեջբերումները

² Ես կասկածում եմ, որ դուք կարող եք փորձել կիրառել Խափանողի օրինակը

³ տե՛ս Հիշողության քարտեզագրված ուժեղացման rttree-ի համար պահանջվող չափի գնահատում

4-ը managed_shared_memory-ը փոխարինեց manage_mapped_file-ով և նվազեցրեց հզորությունները՝ Coliru-ի սահմանափակումների պատճառով

29.06.2020
  • Ավելացվեց բոնուսային բաժինները 30.06.2020
  • Սեհե, շատ շնորհակալ եմ ձեր արժեքավոր մտքերի համար։ Ընդամենը մի քանի հարց. քանի որ ես մտածեցի, որ սա կարող է կարգավորելի լինել, օրինակ. որոշ կոնֆիգուրացիայի ֆայլի օգտագործողների կողմից, դուք գիտեք բազան, օրինակ. հաղորդագրությունների առավելագույն քանակի վրա: 02.07.2020
  • 2. Եթե ես ճիշտ եմ հասկանում, boost::container::scoped_allocator_adaptor-ն աշխատում է ինչպես pmr-ը: 02.07.2020
  • 3. Ուղղակի հետաքրքրության համար, երբ ես ուզում եմ տողեր օգտագործել shmem-ում, ինչու՞ պետք է հայտարարեմ, ինչպես դուք գրել եք bip::basic_string‹char, std::char_traits‹char›, allocator_type› bip::string-ի փոխարեն: Կարծում էի, որ bip::string-ը հենց այդ նպատակով է նախատեսված։ 02.07.2020
  • @bladzio ~Հետաքրքրությունից դրդված~ Սոկրատական ​​ռեժիմը միացված է. 3. ինչո՞ւ եք հարցնում bip::string-ի, բայց ոչ bip::vector-ի մասին, որտեղ դուք նշել եք հատկացուցիչ: Ի՞նչ հատկացուցիչ կօգտագործեր bip::string-ը, եթե այն նախապես միացված լիներ: Ինչպիսի segment_manager-ով այն պետք է մուտքագրվի: 03.07.2020
  • 2, No. PMR-ն այնպես է դարձնում, որ ստատիկ հատկացնողի տեսակը նույնն է հիշողության տարբեր ռեսուրսների համար: Շրջանակային հատկացուցիչները թույլ են տալիս բեռնարկղերին փոխանցել իրենց հատկացուցիչը իրենց տարրերին կառուցման համար (օրինակ՝ տեղակայման վրա): PMR _ կիսում է _ այդ վարքը (ենթադրվում է, որ ձեր տարրերը նույնպես կօգտագործեն PMR, ինչը շատ իմաստալից է) 03.07.2020
  • 1. Իմ դիրքորոշումն այն է, որ դուք, որպես կանոն, պետք է ազատ լինեք չափից ավելի մեծացնելու համար (արգելել ֆայլային համակարգերը՝ առանց նոսր ֆայլերի աջակցության, RTOS առանց վիրտուալ հիշողության կամ խիստ ulimit sandboxes, որի դեպքում դուք հավանաբար չեք օգտագործի համօգտագործվող հիշողություն): Եթե ​​դուք պետք է, ապա իսկապես պրոֆիլի հիշողության օգտագործումը: Ես որոշ պատասխաններ ունեմ Stack Overflow ընդհանուր հիշողության օգտագործման պրոֆիլավորման վերաբերյալ (օրինակ՝ stackoverflow.com/a/62344284/85371), ինչպես նաև որոշ պատասխաններ աճող հատվածների վերաբերյալ 03.07.2020
  • ad.3 - Ես կարծում էի, որ վեկտորին տրամադրված հատկացուցիչը կօգտագործվի bip::string :P 4: Ինձ հետաքրքիր է, թե ինչու MyCustomData կոնստրուկտորում SharedFeed կոնստրուկտորին զանգահարելիս դուք արել եք հաղորդագրություններ_(հզորություն, SharedMessage(std::allocator_arg, alloc ), հատկացնել) ? Ես նկատի ունեմ, թե ինչու փոխարենը ոչ հաղորդագրություններ_(հզորություն, հատկացում): 09.07.2020
  • @bladzio Դա պարզապես ծանրաբեռնվածություն է, որը գոյություն ունի հատկացնողի փաստարկը պարզելու համար: Այն ընդհանուր առմամբ օգտակար է ընդհանուր կոդի համար, և նաև ուսանելի այստեղ, քանի որ այն այն է, ինչով ավարտվում է use-allocator-construction-ը (օրինակ՝ կոնտեյների մեջ տեղադրելու ժամանակ): 09.07.2020
  • շատ շնորհակալ եմ բացատրության համար: Ես փորձեցի ընդունել ձեր խորհուրդները՝ հաջորդ քայլն անելու և համատեղ հիշողության մեջ ներդրված կառուցվածքներ կիրառելու համար, բայց այն նույնիսկ չի հավաքում Ինք: Կոմպիլյատորը չի կարողանում գտնել համապատասխան կոնստրուկտոր, բայց ինչու: 13.07.2020
  • Նոր նյութեր

    Օգտագործելով Fetch Vs Axios.Js-ը՝ HTTP հարցումներ կատարելու համար
    JavaScript-ը կարող է ցանցային հարցումներ ուղարկել սերվեր և բեռնել նոր տեղեկատվություն, երբ դա անհրաժեշտ լինի: Օրինակ, մենք կարող ենք օգտագործել ցանցային հարցումը պատվեր ներկայացնելու,..

    Տիրապետել հանգստության արվեստին. մշակողի ուղեցույց՝ ճնշման տակ ծաղկելու համար
    Տիրապետել հանգստության արվեստին. մշակողի ուղեցույց՝ ճնշման տակ ծաղկելու համար Ինչպե՞ս հանգստացնել ձեր միտքը և աշխատեցնել ձեր պրոցեսորը: Ինչպես մնալ հանգիստ և զարգանալ ճնշման տակ...

    Մեքենայի ուսուցում բանկային և ֆինանսների ոլորտում
    Բարդ, խելացի անվտանգության համակարգերը և հաճախորդների սպասարկման պարզեցված ծառայությունները բիզնեսի հաջողության բանալին են: Ֆինանսական հաստատությունները, մասնավորապես, պետք է առաջ մնան կորի..

    Ես AI-ին հարցրի կյանքի իմաստը, այն ինչ ասում էր, ցնցող էր:
    Այն պահից ի վեր, երբ ես իմացա Արհեստական ​​ինտելեկտի մասին, ես հիացած էի այն բանով, թե ինչպես է այն կարողանում հասկանալ մարդկային նորմալ տեքստը, և այն կարող է առաջացնել իր սեփական արձագանքը դրա..

    Ինչպես սովորել կոդավորումը Python-ում վագրի պես:
    Սովորելու համար ծրագրավորման նոր լեզու ընտրելը բարդ է: Անկախ նրանից, թե դուք սկսնակ եք, թե առաջադեմ, դա օգնում է իմանալ, թե ինչ թեմաներ պետք է սովորել: Ծրագրավորման լեզվի հիմունքները, դրա..

    C++-ի օրական բիթ(ե) | Ամենաերկար պալինդրոմային ենթաշարը
    C++ #198-ի ամենօրյա բիթ(ե), Ընդհանուր հարցազրույցի խնդիր. Ամենաերկար պալինդրոմային ենթատող: Այսօր մենք կանդրադառնանք հարցազրույցի ընդհանուր խնդրին. Ամենաերկար palindromic substring...

    Kydavra ICAReducer՝ ձեր տվյալների ծավալայինությունը նվազեցնելու համար
    Ի՞նչ է ICAReducer-ը: ICAReducer-ն աշխատում է հետևյալ կերպ. այն նվազեցնում է նրանց միջև բարձր փոխկապակցված հատկանիշները մինչև մեկ սյունակ: Բավականին նման է PCAreducer-ին, չնայած այն..