首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >智能指针包装惩罚。用std::map进行注释

智能指针包装惩罚。用std::map进行注释
EN

Stack Overflow用户
提问于 2013-04-12 05:13:55
回答 4查看 883关注 0票数 6

我目前正处于一个非常重要的项目中。以下是我对这个问题的一些问题。

Question1

我的项目涉及很多boost::shared_ptr.I,知道使用boost::make_shared在运行中创建共享指针很慢,因为它需要跟踪引用,因此开销很大。我想知道,如果boost共享指针已经创建,那么这两个语句是具有相同的性能还是一个比另一个更快呢?如果常规指针更快,而且我已经有了共享指针,那么有什么选项可以调用共享指针所指向的方法呢?

代码语言:javascript
复制
 statement1: sharedptr->someMethod();  //here the pointer is a shared ptr created by boost::make_shared
 statement2: regularptr->someMethod(); //here the pointer is a regular one made with new

问题2

我有一个实例方法,它被快速调用,它每次在堆栈上创建一个std::vector<std::string>。我决定将该向量指针存储在静态std::map (即std::map<std::String,std::vector<std::string>*> )中。如果该键的映射中不存在向量(这可能是方法的名称)。创建有效的向量地址并将其添加到map.So中,我的问题是“是否值得搜索一个向量地址并返回一个有效地址,只需在堆栈上像std::vector<std::string> somevector一样创建一个有效地址,我也想知道std::map查找的性能。

任何有关这些关切的意见都将不胜感激。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-04-12 05:58:25

回答Q#1

如果常规指针更快,而且我已经有了共享指针,那么有什么选项可以调用共享指针所指向的方法呢?

operator-> boost::shared_ptr 有断言

代码语言:javascript
复制
typename boost::detail::sp_member_access< T >::type operator-> () const 
{
    BOOST_ASSERT( px != 0 );
    return px;
}

因此,首先,请确保您已经定义了NDEBUG (通常在发布版本中它是自动完成的):

代码语言:javascript
复制
#define NDEBUG

我对boost::shared_ptr的取消引用和原始指针之间的汇编程序进行了比较:

代码语言:javascript
复制
template<int tag,typename T>
NOINLINE void test(const T &p)
{
    volatile auto anti_opti=0;
    ASM_MARKER<tag+0>();
    anti_opti = p->data;
    anti_opti = p->data;
    ASM_MARKER<tag+1>();
    (void)anti_opti;
}
代码语言:javascript
复制
test<1000>(new Foo);

ASMtest时,T代码是Foo* (不要害怕,下面有diff ):

代码语言:javascript
复制
_Z4testILi1000EP3FooEvRKT0_:
.LFB4088:
.cfi_startproc
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset 3, -16
movq %rdi, %rbx
subq $16, %rsp
.cfi_def_cfa_offset 32
movl $0, 12(%rsp)
call _Z10ASM_MARKERILi1000EEvv
movq (%rbx), %rax
movl (%rax), %eax
movl %eax, 12(%rsp)
movl %eax, 12(%rsp)
call _Z10ASM_MARKERILi1001EEvv
movl 12(%rsp), %eax
addq $16, %rsp
.cfi_def_cfa_offset 16
popq %rbx
.cfi_def_cfa_offset 8
ret
.cfi_endproc
代码语言:javascript
复制
test<2000>(boost::make_shared<Foo>());

ASM代码在Tboost::shared_ptr<Foo>时的T代码

代码语言:javascript
复制
_Z4testILi2000EN5boost10shared_ptrI3FooEEEvRKT0_:
.LFB4090:
.cfi_startproc
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset 3, -16
movq %rdi, %rbx
subq $16, %rsp
.cfi_def_cfa_offset 32
movl $0, 12(%rsp)
call _Z10ASM_MARKERILi2000EEvv
movq (%rbx), %rax
movl (%rax), %eax
movl %eax, 12(%rsp)
movl %eax, 12(%rsp)
call _Z10ASM_MARKERILi2001EEvv
movl 12(%rsp), %eax
addq $16, %rsp
.cfi_def_cfa_offset 16
popq %rbx
.cfi_def_cfa_offset 8
ret
.cfi_endproc

下面是diff -U 0 foo_p.asm shared_ptr_foo_p.asm命令的输出:

代码语言:javascript
复制
--- foo_p.asm   Fri Apr 12 10:38:05 2013
+++ shared_ptr_foo_p.asm        Fri Apr 12 10:37:52 2013
@@ -1,2 +1,2 @@
-_Z4testILi1000EP3FooEvRKT0_:
-.LFB4088:
+_Z4testILi2000EN5boost10shared_ptrI3FooEEEvRKT0_:
+.LFB4090:
@@ -11 +11 @@
-call _Z10ASM_MARKERILi1000EEvv
+call _Z10ASM_MARKERILi2000EEvv
@@ -16 +16 @@
-call _Z10ASM_MARKERILi1001EEvv
+call _Z10ASM_MARKERILi2001EEvv

正如您所看到的,不同之处仅在于函数签名,而tag非类型模板参数值,代码的其余部分是。

一般来说-- shared_ptr非常昂贵--它的引用计数在线程之间是同步的(通常通过原子操作)。如果您要使用boost::intrusive_ptr,那么您可以实现您自己的increment/decrement,而不需要线程同步,这将加速引用计数。

如果您能够负担得起使用unique_ptr或移动语义(通过Boost.Move或C++11) -那么将不会有任何引用计数-它将更快。

带有ASM输出的现场演示

代码语言:javascript
复制
#define NDEBUG

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#define NOINLINE __attribute__ ((noinline))

template<int>
NOINLINE void ASM_MARKER()
{
    volatile auto anti_opti = 11;
    (void)anti_opti;
}

struct Foo
{
    int data;
};

template<int tag,typename T>
NOINLINE void test(const T &p)
{
    volatile auto anti_opti=0;
    ASM_MARKER<tag+0>();
    anti_opti = p->data;
    anti_opti = p->data;
    ASM_MARKER<tag+1>();
    (void)anti_opti;
}

int main()
{
    {
        auto p = new Foo;
        test<1000>(p);
        delete p;
    }
    {
        test<2000>(boost::make_shared<Foo>());
    }
}

回答Q#2

我有一个实例方法,它被快速调用,它每次在堆栈上创建一个std::向量。

一般来说,尝试重用vector的容量以防止代价高昂的再分配是个好主意。例如,最好是替换:

代码语言:javascript
复制
{
    for(/*...*/)
    {
        std::vector<value> temp;
        // do work on temp
    }
}

通过以下方式:

代码语言:javascript
复制
{
    std::vector<value> temp;
    for(/*...*/)
    {
        // do work on temp
        temp.clear();
    }
}

但是看起来,由于std::map<std::string,std::vector<std::string>*>类型,您正在尝试执行某种回忆录

如前所述,与具有std::map O(ln(N))查找/插入的boost::unordered_map/std::unordered_map不同,您可以尝试使用具有O(1) average和O(N)最坏情况复杂度的boost::unordered_map/std::unordered_map进行查找/插入,以及更好的局部性/紧凑性(对缓存友好)。

另外,局外人也要试试Boost.Flyweight

富勒权值是小规模的句柄类,允许对共享的公共数据进行持续访问,从而允许在合理的内存限制范围内管理大量实体。Boost.Flyweight通过提供类模板FlyW事重(作为const T的插入替代)使使用这个常见的编程习语变得更加容易。

票数 13
EN

Stack Overflow用户

发布于 2013-04-12 05:50:29

对于Question1:

在架构设计、所使用的算法以及只有在高层设计强大的情况下,低层关注点才能获得重要的性能增益。让我们来问一下您的问题,常规指针性能高于shared_ptr。但是,您看到的不使用shared_ptr的开销也更多,这增加了代码长期运行的维护成本。在性能关键的应用程序中,必须避免冗余对象的创建和销毁。在这种情况下,shared_ptr发挥着重要作用,它通过减少释放资源的开销来共享公共对象,跨越线程。是的,共享指针比普通指针消耗更多的时间,因为有了重新计数、分配(对象、计数器、删除器)等等,您可以通过防止不必要的them.use作为参考( shared_ptr const&)复制来使them.use更快。此外,在您不需要共享资源的情况下,跨线程不使用shared_ptr,常规ptr将在这种情况下提供更好的性能。

问题2

如果想要使用shared_ptr对象的重用池,可以更好地研究对象池设计模式方法。模式

票数 4
EN

Stack Overflow用户

发布于 2013-04-12 06:21:15

问题1:

我在我的项目中广泛使用共享指针,但我不想使用shared_ptr<T>。它需要一个与T本身分开分配的堆对象,因此内存分配开销增加了一倍,内存使用量增加了一定数量,这取决于运行时库的实现。intrusive_ptr更有效率,但是有一个关键问题让我感到厌烦,那就是函数调用:

代码语言:javascript
复制
void Foo(intrusive_ptr<T> x) {...}

每次调用Foo时,参数x的引用计数必须以相对昂贵的原子增量递增,然后在输出时减少。但是这是多余的,因为您通常可以假设调用方已经有了对x的引用,并且在调用期间引用是有效的。有一些可能的方式,调用者可能已经没有引用,但是编写代码并不难,因为调用者的引用总是有效的。

因此,我更喜欢使用与intrusive_ptr相同的自己的智能指针类,只是它隐式地转换到T*和T*。然后,我总是声明我的方法来获取简单的指针,避免不必要的引用计数:

代码语言:javascript
复制
void Foo(T* x) {...}

这种方法在我的项目中被证明是有效的,但老实说,我从来没有真正衡量过它所带来的性能差异。

此外,在可能的情况下,更喜欢使用auto_ptr (C++03)或unique_ptr (C++11)。

问题2:

我不明白您为什么要使用std::map。首先,hash_map会更快(只要它不是‘s 2008/2010,细节在这里某处中的VC++ Dinkumware实现),其次,如果每个方法只需要一个向量,为什么不使用一个静态变量std::vector<std::string>

如果每次调用方法时都必须在哈希表中查找向量,那么我的猜测是,与每次创建新的向量相比,您节省了很少或根本没有时间。如果在std::map中查找向量,则需要更长的时间。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15963563

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档