我目前正处于一个非常重要的项目中。以下是我对这个问题的一些问题。
Question1
我的项目涉及很多boost::shared_ptr.I,知道使用boost::make_shared在运行中创建共享指针很慢,因为它需要跟踪引用,因此开销很大。我想知道,如果boost共享指针已经创建,那么这两个语句是具有相同的性能还是一个比另一个更快呢?如果常规指针更快,而且我已经有了共享指针,那么有什么选项可以调用共享指针所指向的方法呢?
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查找的性能。
任何有关这些关切的意见都将不胜感激。
发布于 2013-04-12 05:58:25
回答Q#1
如果常规指针更快,而且我已经有了共享指针,那么有什么选项可以调用共享指针所指向的方法呢?
operator-> boost::shared_ptr 有断言
typename boost::detail::sp_member_access< T >::type operator-> () const
{
BOOST_ASSERT( px != 0 );
return px;
}因此,首先,请确保您已经定义了NDEBUG (通常在发布版本中它是自动完成的):
#define NDEBUG我对boost::shared_ptr的取消引用和原始指针之间的汇编程序进行了比较:
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;
}test<1000>(new Foo);当ASM是test时,T代码是Foo* (不要害怕,下面有diff ):
_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_endproctest<2000>(boost::make_shared<Foo>());ASM代码在T为boost::shared_ptr<Foo>时的T代码
_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命令的输出:
--- 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) -那么将不会有任何引用计数-它将更快。
#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的容量以防止代价高昂的再分配是个好主意。例如,最好是替换:
{
for(/*...*/)
{
std::vector<value> temp;
// do work on temp
}
}通过以下方式:
{
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的插入替代)使使用这个常见的编程习语变得更加容易。
发布于 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对象的重用池,可以更好地研究对象池设计模式方法。模式
发布于 2013-04-12 06:21:15
问题1:
我在我的项目中广泛使用共享指针,但我不想使用shared_ptr<T>。它需要一个与T本身分开分配的堆对象,因此内存分配开销增加了一倍,内存使用量增加了一定数量,这取决于运行时库的实现。intrusive_ptr更有效率,但是有一个关键问题让我感到厌烦,那就是函数调用:
void Foo(intrusive_ptr<T> x) {...}每次调用Foo时,参数x的引用计数必须以相对昂贵的原子增量递增,然后在输出时减少。但是这是多余的,因为您通常可以假设调用方已经有了对x的引用,并且在调用期间引用是有效的。有一些可能的方式,调用者可能已经没有引用,但是编写代码并不难,因为调用者的引用总是有效的。
因此,我更喜欢使用与intrusive_ptr相同的自己的智能指针类,只是它隐式地转换到T*和T*。然后,我总是声明我的方法来获取简单的指针,避免不必要的引用计数:
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中查找向量,则需要更长的时间。
https://stackoverflow.com/questions/15963563
复制相似问题