mutable
mutableは、クラスのconstメンバ関数(メソッド)の中でも操作できるメンバ変数を作る場合に指定する。
具体的にどういう場合にこれを使うかについてだが、例えばこのようにgetFugaの呼ばれた回数をひそかに保持しておくとか、
1 2 3 4 5 6 7 8 9 10 11 |
class Hoge { mutable int _counter; int getFuga() const { _counter++; return _fuga; } } |
ある更新があった場合だけ文字列を更新してあげるなどが考えられる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Hoga { mutable bool _update; string strHege; string getHege() const { if( _update ) { strHege = getHoga(); _update = false; } return strHege; } } |
そもそもmutableという英語の意味は、goo辞書を参照して
変りやすい, 移り気の
という意味のようだ。そもそも値を少しでも更新するならconstなど使うなという話でもあるが、パフォーマンスは向上させたいが、意味的にはconstなどという場合はやはりあるようでそんな場合には使えるのではないかと。
volatile
volatileを変数に付け加えることで、コンパイラの最適化を無効にしレジスタではなく常にメモリにアクセスして変数を利用する。マルチスレッドなどでは、変数がどこでいつ変更されるかわからないので、このような指定が必要だとのこと。
ただ、CriticalSectionを利用してスレッドセーフにしておけばおそらくこのようなことは必要ないので、ここら辺はパフォーマンスの向上のため利用することとなるだろう。ということで次のようなコードで実験してみた。あるグローバル変数に対してvolatileありとなしの変数を1024 * 1024 * 1024回、回してそれを代入するというものである。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
int a; void main() { int n; volatile int m; for( n = 0; n < 1024 * 1024 * 1024; n++ ) { a = n; } for( m = 0; m < 1024 * 1024 * 1024; m++ ) { a = m; } } |
結果は、(いつものようにQueryPerformanceCounterで測定して)前のループが0.469265秒に対し後ろのループは3.632109秒だった(マシンはIntel Core 2 Duo E6600)。ここでmの参照回数が何回かあるのだが、速度的には7〜8倍になってしまっている。逆アセンブリするとコードも変わっており、前のループが下のようになるのに対し、
1 2 3 4 5 6 |
00401037 xor eax,eax 00401039 lea esp,[esp] 00401040 mov dword ptr [a (40337Ch)],eax 00401045 add eax,1 00401048 cmp eax,40000000h 0040104D jl main+40h (401040h) |
後ろのループでは、
1 2 3 4 5 6 7 8 9 10 |
00401101 mov dword ptr [esp+20h],ebp 00401105 mov edx,dword ptr [esp+20h] 00401109 cmp edx,40000000h 0040110F jge main+12Bh (40112Bh) 00401111 mov eax,dword ptr [esp+20h] 00401115 mov dword ptr [a (40337Ch)],eax 0040111A add dword ptr [esp+20h],1 0040111F mov ecx,dword ptr [esp+20h] 00401123 cmp ecx,40000000h 00401129 jl main+111h (401111h) |
となっていた。たしかに毎度読み込んでいることがわかる。(ちなみに、Release最適化ビルド)
こちらのvolatileという英語の意味は、goo辞書を参照して
揮発性の; 移り気の
という意味らしい。揮発性というのはよく聞くが、移り気のとう意味はmutableとかぶっている。