Q#1 的答案
如果常规指针更快并且我已经拥有共享指针,我有哪些选项可以调用共享指针指向的方法?
operator-> 内boost::shared_ptr has assertion:
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);
ASMtest 的代码当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_endproc
test<2000>(boost::make_shared<Foo>());
当T 为boost::shared_ptr<Foo> 时test 的ASM 代码:
_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非类型模板参数值,其余代码为IDENTICAL。
一般 - shared_ptr 非常昂贵 - 它的引用计数在线程之间同步(通常通过原子操作)。如果您改用boost::intrusive_ptr,那么您可以在没有线程同步的情况下实现自己的increment/decrement,这样可以加快引用计数。
如果您负担得起使用 unique_ptr 或移动语义(通过 Boost.Move 或 C++11) - 那么将不会有任何引用计数 - 它会更快。
LIVE DEMO WITH ASM OUTPUT
#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。
一般来说,尝试重用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>*>,您正试图执行某种memoization。
正如已经建议的那样,您可以尝试使用具有 O(ln(N)) 查找/插入的 std::map 而不是具有 O( 1) 查找/插入的平均和 O(N) 最坏情况复杂度,以及更好的局部性/紧凑性(缓存友好)。
另外,一起试试Boost.Flyweight:
享元是小型句柄类,授予对共享公共数据的持续访问权限,从而允许在合理的内存限制内管理大量实体。 Boost.Flyweight 通过提供类模板 flyweight 可以轻松使用这种常见的编程习惯,该类模板可替代 const T。