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_endproc
test<2000>(boost::make_shared<Foo>());
ASM
のコードtest
は:T
boost::shared_ptr<Foo>
_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
をスレッド同期なしで実装できます。これにより、参照カウントが高速化されます。
セマンティックを ( Boost.Moveunique_ptr
または C++11を介して) 使用または移動する余裕がある場合は、参照カウントがなくなり、さらに高速になります。
ASM 出力を使用したライブ デモ
#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を実行しようとしているようです。
すでに示唆されているように、std::map
これはO(ln(N))ルックアップ/挿入の代わりに、/ を使用しようとすることができますboost::unordered_map
/std::unordered_map
これは、ルックアップ/挿入のO(1)平均とO(N)最悪の場合の複雑さ、およびより良い局所性/コンパクト性 (キャッシュフレンドリー)。
また、Boost.Flyweight を試してみてください:
Flyweightsは、共有共通データへの常時アクセスを許可する小さなサイズのハンドル クラスであるため、妥当なメモリ制限内で大量のエンティティを管理できます。Boost.Flyweightは、 const Tのドロップイン置換として機能するクラス テンプレートflyweightを提供することで、この一般的なプログラミング イディオムを簡単に使用できるようにします。