草庐IT

c++ - 没有内存重新分配的 std::set 的替代方案?

coder 2023-06-02 原文

在一个应用程序中,我详尽地生成了许多子问题,并使用“std::set”操作来解决它们。为此,我需要在排序列表上“insert”和“find”元素以及“迭代”。

问题在于,对于数百万个子问题中的每一个,每次我在集合中插入一个元素时,“std::set”实现都会分配新的内存,这使得整个应用程序非常慢:

{   // allocate a non-value node
    _Nodeptr _Pnode = this->_Getal().allocate(1); // <- bottleneck of the program

是否有一些 STL 结构允许我在“O(log(n))”中进行上述操作而不重新分配任何内存?

最佳答案

使用自定义分配器似乎可以减少构建和发布 std::set<...> 所花费的时间。 .下面是一个简单分配器的完整演示以及一个分析结果时间的程序。

#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <memory>
#include <set>
#include <vector>

// ----------------------------------------------------------------------------

template <typename T, std::size_t pool_size = 1024>
class pool_allocator
{
private:
    std::vector<T*> d_pools;
    T*              d_next;
    T*              d_end;
public:
    template <typename O>
    struct rebind {
        typedef pool_allocator<O, pool_size> other;
    };
    pool_allocator(): d_next(), d_end() {}
    ~pool_allocator() {
        std::for_each(this->d_pools.rbegin(), this->d_pools.rend(),
                      [](T* memory){ operator delete(memory); });
    }
    typedef T value_type;
    T*   allocate(std::size_t n) {
        if (std::size_t(this->d_end - this->d_next) < n) {
            if (pool_size < n) {
                // custom allocation for bigger number of objects
                this->d_pools.push_back(static_cast<T*>(operator new(sizeof(T) * n)));
                return this->d_pools.back();
            }
            this->d_pools.push_back(static_cast<T*>(operator new(sizeof(T) * pool_size)));
            this->d_next = this->d_pools.back();
            this->d_end  = this->d_next + pool_size;
        }
        T* rc(this->d_next);
        this->d_next += n;
        return rc;
    }
    void deallocate(T*, std::size_t) {
        // this could try to recycle buffers
    }
};

// ----------------------------------------------------------------------------

template <typename Allocator>
void time(char const* name, std::vector<int> const& random) {
    std::cout << "running " << name << std::flush;
    using namespace std::chrono;
    high_resolution_clock::time_point start(high_resolution_clock::now());

    std::size_t size(0);
    {
        std::set<int, std::less<int>, Allocator> values;
        for (int value: random) {
            values.insert(value);
        }
        size = values.size();
    }

    high_resolution_clock::time_point end(high_resolution_clock::now());
    std::cout << ": size=" << size << " time="
              << duration_cast<milliseconds>(end - start).count() << "ms\n";
}

// ----------------------------------------------------------------------------

int main()
{
    std::cout << "preparing..." << std::flush;
    std::size_t count(10000000);
    std::vector<int> random;
    random.reserve(count);
    std::generate_n(std::back_inserter(random), count, [](){ return std::rand(); });
    std::cout << "done\n";

    time<std::allocator<int>>("default allocator      ", random);
    time<pool_allocator<int, 32>>("custom allocator (32)  ", random);
    time<pool_allocator<int, 256>>("custom allocator (256) ", random);
    time<pool_allocator<int, 1024>>("custom allocator (1024)", random);
    time<pool_allocator<int, 2048>>("custom allocator (2048)", random);
    time<pool_allocator<int, 4096>>("custom allocator (4096)", random);
    time<std::allocator<int>>("default allocator      ", random);
}

// results from clang/libc++:
// preparing...done
// running default allocator      : size=10000000 time=13927ms
// running custom allocator (32)  : size=10000000 time=9260ms
// running custom allocator (256) : size=10000000 time=9511ms
// running custom allocator (1024): size=10000000 time=9172ms
// running custom allocator (2048): size=10000000 time=9153ms
// running custom allocator (4096): size=10000000 time=9599ms
// running default allocator      : size=10000000 time=13730ms

// results from gcc/libstdc++:
// preparing...done
// running default allocator      : size=10000000 time=15814ms
// running custom allocator (32)  : size=10000000 time=10868ms
// running custom allocator (256) : size=10000000 time=10229ms
// running custom allocator (1024): size=10000000 time=10556ms
// running custom allocator (2048): size=10000000 time=10392ms
// running custom allocator (4096): size=10000000 time=10664ms
// running default allocator      : size=10000000 time=17941ms

关于c++ - 没有内存重新分配的 std::set 的替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24096466/

有关c++ - 没有内存重新分配的 std::set 的替代方案?的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

    我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

  3. ruby-on-rails - rails 目前在重启后没有安装 - 2

    我有一个奇怪的问题:我在rvm上安装了ruby​​onrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  6. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  7. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  8. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

  9. ruby-on-rails - active_admin 目录中的常量警告重新声明 - 2

    我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA

  10. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

随机推荐