読者です 読者をやめる 読者になる 読者になる

ゆとりーなの日記

日記的な事を書いて行くと思はれる

最適化かかるらしい

自作ライブラリの実装の話です。

template <typename Drawable>
class draw : private boost::noncopyable {
 public:
  draw(const device_handle &device, const Drawable &drawable) : device_(device), drawable_(drawable) {
    common_draw();
  }

 private:
  void common_draw() const {
    assert(device_);
    const scoped_texture_stage<typename Drawable::texture_stage_tuple_type> texture_stage(device_);
    const scoped_addressing<typename Drawable::addressing_tuple_type> addressing(device_);
    const scoped_blend<typename Drawable::blend_pair_type> blend(device_);
    set_texture(device_, drawable_);
    const auto vertex = drawable_access::copy_vertex(drawable_);
    device_->SetFVF(Drawable::fvf_type::fvf_type::value);
    device_->DrawPrimitiveUP(Drawable::primitive_type::type::value,
                             Drawable::count_type::value, 
                             vertex.data(), 
                             sizeof(vertex.front()));
  }

  const device_handle &device_;
  const Drawable &drawable_;
};

現状描画関数の実態はこんな感じになってます。scoped_なんたらはDrawableがブレンドモード等を書き換えるタグを持っていた場合、コンストラクタでタグが指定するモードに切り替え、デストラクタでデフォルトに戻すみたいなことをやってます。タグを持っていないときは何もしないクラスに展開されるようにしかけてあります。

template <typename BlendPair, typename T = void>
class scoped_blend;

template <typename BlendPair>
class scoped_blend<BlendPair, typename std::enable_if<!std::is_same<void, BlendPair>::value>::type> {
 public:
  explicit scoped_blend(const device_handle &device) : device_(device) {
    assert(device_);
    set_blend_pair<BlendPair>(device_);
  }

  ~scoped_blend() {
    assert(device_);
    set_blend_pair<default_blend>(device_);
  }

 private:
  const device_handle &device_;
};

template <typename BlendPair>
class scoped_blend<BlendPair, typename std::enable_if<std::is_same<void, BlendPair>::value>::type> {
 public:
  explicit scoped_blend(const device_handle &) {}
};

これ、何もしないクラスの展開されたときに最適化がかかってクラスの存在自体が消えてくれるのかが気になったのでアセンブリ出力してみて覗いてみました。
取り敢えずこんな感じのコードのアセンブリを出してみました。

#include <ngy313/process/process.hpp>
#include <ngy313/window/window.hpp>
#include <ngy313/graphic/graphic.hpp>
#include <ngy313/graphic/scoped_render.hpp>
#include <ngy313/graphic/image.hpp>
#include <ngy313/graphic/blend.hpp>
#include <ngy313/graphic/sprite.hpp>
#include <ngy313/graphic/colored.hpp>
#include <ngy313/utility/color_code.hpp>

int main() {
  ngy313::window::show();
  const ngy313::graphic::image back("test3.png");
  const ngy313::graphic::image image("test4.png");
  return ngy313::process::run([&] {
    {
      const ngy313::graphic::scoped_render render;
      if (render.succeeded()) {
        ngy313::graphic::clear_screen(ngy313::utility::kBlack);
        ngy313::graphic::draw(ngy313::graphic::sprite(0.f, 0.f, back));
        ngy313::graphic::draw(ngy313::graphic::sprite(0.f, 0.f, image));
        ngy313::graphic::draw(ngy313::graphic::sprite(image.width(), 0.f, image) | 
                              ngy313::graphic::colored(0x80FFFFFF));
        ngy313::graphic::draw(ngy313::graphic::sprite(0.f, image.height(), image) | 
                              ngy313::graphic::add_blend);
        ngy313::graphic::draw(ngy313::graphic::sprite(image.width(), image.height(), image) | 
                              ngy313::graphic::colored(0xCCFFFFFF) |
                              ngy313::graphic::sub_blend);
        ngy313::graphic::draw(ngy313::graphic::sprite(0.f, image.height() * 2.f, image) | 
                              ngy313::graphic::mul_blend);
      }
    }
    ngy313::graphic::present();
  });
}

以下アセンブリ
ngy313::graphic::detail::draw::common_drawの場合(オプションは変えないのでscoped_なんたら系の部分は消されているのが理想)

; 21   :   void common_draw() const {

  00000	55		 push	 ebp
  00001	8b ec		 mov	 ebp, esp
  00003	81 ec e8 00 00
	00		 sub	 esp, 232		; 000000e8H
  00009	a1 00 00 00 00	 mov	 eax, DWORD PTR ___security_cookie
  0000e	33 c5		 xor	 eax, ebp
  00010	89 45 fc	 mov	 DWORD PTR __$ArrayPad$[ebp], eax

; 22   :     assert(device_);
; 23   :     // コメント
; 24   :     // コメント
; 25   :     const scoped_texture_stage<typename Drawable::texture_stage_tuple_type> texture_stage(device_);
; 26   :     const scoped_addressing<typename Drawable::addressing_tuple_type> addressing(device_);
; 27   :     const scoped_blend<typename Drawable::blend_pair_type> blend(device_);
; 28   :     set_texture(device_, drawable_);

  00013	8b 53 04	 mov	 edx, DWORD PTR [ebx+4]
  00016	8b 12		 mov	 edx, DWORD PTR [edx]
  00018	8b 03		 mov	 eax, DWORD PTR [ebx]
  0001a	8b 00		 mov	 eax, DWORD PTR [eax]
  0001c	8b 08		 mov	 ecx, DWORD PTR [eax]
  0001e	56		 push	 esi
  0001f	57		 push	 edi
  00020	52		 push	 edx
  00021	6a 00		 push	 0
  00023	50		 push	 eax
  00024	8b 81 04 01 00
	00		 mov	 eax, DWORD PTR [ecx+260]
  0002a	ff d0		 call	 eax

; 29   :     const auto vertex = drawable_access::copy_vertex(drawable_);

  0002c	8b 4b 04	 mov	 ecx, DWORD PTR [ebx+4]
  0002f	8d 85 18 ff ff
	ff		 lea	 eax, DWORD PTR $T405436[ebp]
  00035	e8 00 00 00 00	 call	 ?vertex@sprite@graphic@ngy313@@ABE?AV?$array@U?$vertex@U?$vector3@Udimension2_fvf_tag@graphic@ngy313@@Udiffuse_fvf_tag@23@Utex1_fvf_tag@23@@mpl@boost@@$02@graphic@ngy313@@$03@tr1@std@@XZ ; ngy313::graphic::sprite::vertex
  0003a	8b f0		 mov	 esi, eax
  0003c	b9 1c 00 00 00	 mov	 ecx, 28			; 0000001cH
  00041	8d 7d 88	 lea	 edi, DWORD PTR _vertex$[ebp]
  00044	f3 a5		 rep movsd

; 30   :     device_->SetFVF(Drawable::fvf_type::fvf_type::value);

  00046	8b 0b		 mov	 ecx, DWORD PTR [ebx]
  00048	8b 01		 mov	 eax, DWORD PTR [ecx]
  0004a	8b 10		 mov	 edx, DWORD PTR [eax]
  0004c	68 44 01 00 00	 push	 324			; 00000144H
  00051	50		 push	 eax
  00052	8b 82 64 01 00
	00		 mov	 eax, DWORD PTR [edx+356]
  00058	ff d0		 call	 eax

; 31   :     device_->DrawPrimitiveUP(Drawable::primitive_type::type::value,
; 32   :                              Drawable::count_type::value, 
; 33   :                              vertex.data(), 
; 34   :                              sizeof(vertex.front()));

  0005a	8b 0b		 mov	 ecx, DWORD PTR [ebx]
  0005c	8b 01		 mov	 eax, DWORD PTR [ecx]
  0005e	8b 10		 mov	 edx, DWORD PTR [eax]
  00060	8b 92 4c 01 00
	00		 mov	 edx, DWORD PTR [edx+332]
  00066	6a 1c		 push	 28			; 0000001cH
  00068	8d 4d 88	 lea	 ecx, DWORD PTR _vertex$[ebp]
  0006b	51		 push	 ecx
  0006c	6a 02		 push	 2
  0006e	6a 05		 push	 5
  00070	50		 push	 eax
  00071	ff d2		 call	 edx

; 35   :   }

  00073	8b 4d fc	 mov	 ecx, DWORD PTR __$ArrayPad$[ebp]
  00076	5f		 pop	 edi
  00077	33 cd		 xor	 ecx, ebp
  00079	5e		 pop	 esi
  0007a	e8 00 00 00 00	 call	 @__security_check_cookie@4
  0007f	8b e5		 mov	 esp, ebp
  00081	5d		 pop	 ebp
  00082	c3		 ret	 0

この出力を見る限りだと、scoped_なんたら系の生成はごっそり抜けているように見えます。
ngy313::graphic::detail::draw,std::tr1::integral_constant > > > >::common_drawの場合(ブレンドモードを切り替える)

; 21   :   void common_draw() const {

  00000	55		 push	 ebp
  00001	8b ec		 mov	 ebp, esp
  00003	6a ff		 push	 -1
  00005	68 00 00 00 00	 push	 __ehhandler$?common_draw@?$draw@U?$add_drawable_adaptor@Vsprite@graphic@ngy313@@U?$add_blend_pair@Vsprite@graphic@ngy313@@U?$blend_pair@U?$integral_constant@W4_D3DBLEND@@$00@tr1@std@@U?$integral_constant@W4_D3DBLEND@@$02@23@@23@@23@@graphic@ngy313@@@detail@graphic@ngy313@@ABEXXZ
  0000a	64 a1 00 00 00
	00		 mov	 eax, DWORD PTR fs:0
  00010	50		 push	 eax
  00011	81 ec ec 00 00
	00		 sub	 esp, 236		; 000000ecH
  00017	a1 00 00 00 00	 mov	 eax, DWORD PTR ___security_cookie
  0001c	33 c5		 xor	 eax, ebp
  0001e	89 45 f0	 mov	 DWORD PTR __$ArrayPad$[ebp], eax
  00021	53		 push	 ebx
  00022	56		 push	 esi
  00023	57		 push	 edi
  00024	50		 push	 eax
  00025	8d 45 f4	 lea	 eax, DWORD PTR __$EHRec$[ebp]
  00028	64 a3 00 00 00
	00		 mov	 DWORD PTR fs:0, eax
  0002e	8b d9		 mov	 ebx, ecx

; 22   :     assert(device_);
; 23   :     // コメント
; 24   :     // コメント
; 25   :     const scoped_texture_stage<typename Drawable::texture_stage_tuple_type> texture_stage(device_);
; 26   :     const scoped_addressing<typename Drawable::addressing_tuple_type> addressing(device_);
; 27   :     const scoped_blend<typename Drawable::blend_pair_type> blend(device_);

  00030	8b 33		 mov	 esi, DWORD PTR [ebx]
  00032	8b 06		 mov	 eax, DWORD PTR [esi]
  00034	8b 08		 mov	 ecx, DWORD PTR [eax]
  00036	8b 91 e4 00 00
	00		 mov	 edx, DWORD PTR [ecx+228]
  0003c	6a 01		 push	 1
  0003e	6a 13		 push	 19			; 00000013H
  00040	50		 push	 eax
  00041	89 b5 7c ff ff
	ff		 mov	 DWORD PTR _blend$[ebp], esi
  00047	ff d2		 call	 edx
  00049	8b 06		 mov	 eax, DWORD PTR [esi]
  0004b	8b 08		 mov	 ecx, DWORD PTR [eax]
  0004d	8b 91 e4 00 00
	00		 mov	 edx, DWORD PTR [ecx+228]
  00053	6a 03		 push	 3
  00055	6a 14		 push	 20			; 00000014H
  00057	50		 push	 eax
  00058	ff d2		 call	 edx
  0005a	c7 45 fc 00 00
	00 00		 mov	 DWORD PTR __$EHRec$[ebp+8], 0

; 28   :     set_texture(device_, drawable_);

  00061	8b 4b 04	 mov	 ecx, DWORD PTR [ebx+4]
  00064	8b 09		 mov	 ecx, DWORD PTR [ecx]
  00066	8b 03		 mov	 eax, DWORD PTR [ebx]
  00068	8b 00		 mov	 eax, DWORD PTR [eax]
  0006a	8b 10		 mov	 edx, DWORD PTR [eax]
  0006c	51		 push	 ecx
  0006d	6a 00		 push	 0
  0006f	50		 push	 eax
  00070	8b 82 04 01 00
	00		 mov	 eax, DWORD PTR [edx+260]
  00076	ff d0		 call	 eax

; 29   :     const auto vertex = drawable_access::copy_vertex(drawable_);

  00078	8b 43 04	 mov	 eax, DWORD PTR [ebx+4]
  0007b	8b 48 0c	 mov	 ecx, DWORD PTR [eax+12]
  0007e	8d 85 08 ff ff
	ff		 lea	 eax, DWORD PTR $T424161[ebp]
  00084	e8 00 00 00 00	 call	 ?vertex@sprite@graphic@ngy313@@ABE?AV?$array@U?$vertex@U?$vector3@Udimension2_fvf_tag@graphic@ngy313@@Udiffuse_fvf_tag@23@Utex1_fvf_tag@23@@mpl@boost@@$02@graphic@ngy313@@$03@tr1@std@@XZ ; ngy313::graphic::sprite::vertex
  00089	8b f0		 mov	 esi, eax

; 30   :     device_->SetFVF(Drawable::fvf_type::fvf_type::value);

  0008b	8b 03		 mov	 eax, DWORD PTR [ebx]
  0008d	b9 1c 00 00 00	 mov	 ecx, 28			; 0000001cH
  00092	8d 7d 80	 lea	 edi, DWORD PTR _vertex$[ebp]
  00095	f3 a5		 rep movsd
  00097	8b 00		 mov	 eax, DWORD PTR [eax]
  00099	8b 08		 mov	 ecx, DWORD PTR [eax]
  0009b	8b 91 64 01 00
	00		 mov	 edx, DWORD PTR [ecx+356]
  000a1	68 44 01 00 00	 push	 324			; 00000144H
  000a6	50		 push	 eax
  000a7	ff d2		 call	 edx

; 31   :     device_->DrawPrimitiveUP(Drawable::primitive_type::type::value,
; 32   :                              Drawable::count_type::value, 
; 33   :                              vertex.data(), 
; 34   :                              sizeof(vertex.front()));

  000a9	8b 1b		 mov	 ebx, DWORD PTR [ebx]
  000ab	8b 03		 mov	 eax, DWORD PTR [ebx]
  000ad	8b 08		 mov	 ecx, DWORD PTR [eax]
  000af	6a 1c		 push	 28			; 0000001cH
  000b1	8d 55 80	 lea	 edx, DWORD PTR _vertex$[ebp]
  000b4	52		 push	 edx
  000b5	6a 02		 push	 2
  000b7	6a 05		 push	 5
  000b9	50		 push	 eax
  000ba	8b 81 4c 01 00
	00		 mov	 eax, DWORD PTR [ecx+332]
  000c0	ff d0		 call	 eax

; 35   :   }

  000c2	c7 45 fc ff ff
	ff ff		 mov	 DWORD PTR __$EHRec$[ebp+8], -1
  000c9	8b b5 7c ff ff
	ff		 mov	 esi, DWORD PTR _blend$[ebp]
  000cf	8b 06		 mov	 eax, DWORD PTR [esi]
  000d1	8b 08		 mov	 ecx, DWORD PTR [eax]
  000d3	8b 91 e4 00 00
	00		 mov	 edx, DWORD PTR [ecx+228]
  000d9	6a 05		 push	 5
  000db	6a 13		 push	 19			; 00000013H
  000dd	50		 push	 eax
  000de	ff d2		 call	 edx
  000e0	8b 06		 mov	 eax, DWORD PTR [esi]
  000e2	8b 08		 mov	 ecx, DWORD PTR [eax]
  000e4	8b 91 e4 00 00
	00		 mov	 edx, DWORD PTR [ecx+228]
  000ea	6a 06		 push	 6
  000ec	6a 14		 push	 20			; 00000014H
  000ee	50		 push	 eax
  000ef	ff d2		 call	 edx
  000f1	8b 4d f4	 mov	 ecx, DWORD PTR __$EHRec$[ebp]
  000f4	64 89 0d 00 00
	00 00		 mov	 DWORD PTR fs:0, ecx
  000fb	59		 pop	 ecx
  000fc	5f		 pop	 edi
  000fd	5e		 pop	 esi
  000fe	5b		 pop	 ebx
  000ff	8b 4d f0	 mov	 ecx, DWORD PTR __$ArrayPad$[ebp]
  00102	33 cd		 xor	 ecx, ebp
  00104	e8 00 00 00 00	 call	 @__security_check_cookie@4
  00109	8b e5		 mov	 esp, ebp
  0010b	5d		 pop	 ebp
  0010c	c3		 ret	 0

オプションなしの時と比較して、なにやらアセンブリコードが出現しています。そして}でスコープを抜ける時のアセンブリコードもかなり増えているので、この増加分がデストラクタ呼び出しに当たりそうです。
というわけでオプションなしの場合は多分クラスの呼び出しが最適化でっそり抜けてくれていると思います。
ほんとは何も書かない場合とも比較したほうがいいのでしょうが・・・。