move semantics を体感したい


## ことの発端

ウチらの開発は非同期処理を記述するケースが多くて値の受け渡しは shared_ptr 使う事が多いので move semantics を積極的に使う場面が少ない。
そんなんで、C++11 から登場している move semantics の恩恵って実感してなかったので、実感しようとしてみたら、意外な結果だったので「何でだろう？」状態。

## 彼（move semantics）との想い出

昔々、move semantics が登場した時期に、

```cpp
std::string f() {
  std::string s = "ABC";
  return std::move(s);
}
```
こんなコード書いて、「オイラ move 使ってるぜ！　どや、メモリコピーが発生しないんやで！」と意気込んでいた頃が懐かしい。。。
その数年後、 ```return std::move(s)``` で std::move は不要という警告が出て、実はこっそり裏で面倒みてくれているという親の愛情みたいなものを感じつつ、チームのメンバーには内緒で std::move を削除していたわ。（苦笑）


## 閑話休題

それはそうと、実際にどんな感じに実装されているんだろうって事で、std::stringの結果を確認しようと思った。

```cpp
#include <iostream>

template <typename T> void display(const char* name, const T& x, const void* p1, const void* p2) {
  std::cout << name << ", " << x
  << ", this = " << std::hex << p1
  << ", c_str() = " << std::hex << p2
  << std::endl;
}

void case1()
{
  std::string s = "ABC";    
  display("s"   , s     , &s    , s.c_str());
  std::string ss(std::move(s));
  display("s"   , s     , &s    , s.c_str());
  display("ss"  , ss    , &ss   , ss.c_str());
}


void case2()
{
  std::string ss;
  std::string s = "ABC";    
  display("s"   , s     , &s    , s.c_str());
  display("ss"  , ss    , &ss   , ss.c_str());
  std::string = std::move(s);
  display("s"   , s     , &s    , s.c_str());
  display("ss"  , ss    , &ss   , ss.c_str());
}

int main(void){
    case1();
    case2();
}
```

無理矢理感があるけど、std::string の s と ss がどんな挙動になるのかと。
「予想では、s のヒープ上のアドレスが ss の文字列になって、sの文字列は初期化されるんじゃないかな？」
だったんだけど、やってみたら、予想が裏切られて「？」になったでござる。

### case1 の結果

| 変数名 | 値 | this のアドレス | c_str() が返却する文字列のアドレス |
| --- | --- | --- | --- |
| s   | ABC | 0x7ffcd9ed0de0 | 0x7ffcd9ed0df0 |
| s |  | 0x7ffcd9ed0de0 | 0x7ffcd9ed0df0 |
| ss | ABC | 0x7ffcd9ed0e00 | 0x7ffcd9ed0e10 |

### case2 の結果

| 変数名 | 値 | this のアドレス | c_str() が返却する文字列のアドレス |
| --- | --- | --- | --- |
| s | ABC | 0x7ffcd9ed0de0 | 0x7ffcd9ed0df0 |
| ss |  | 0x7ffcd9ed0e00 | 0x7ffcd9ed0e10 |
| s |  | 0x7ffcd9ed0de0 | 0x7ffcd9ed0df0 |
| ss |  ABC | 0x7ffcd9ed0e00 | 0x7ffcd9ed0e10 |

### この結果かから言えることは

普通にコピーしているやん！
じゃ〜、std::move なんか使わない方が良い。。。
ん？
いや、ちょい待て。。。
この文字列のアドレスって、スタック上に生成されているんじゃない？

## やりなお〜し

と、言うことで、 ```std::string(1024, 'A')``` って感じで初期化して再実行

### case1 の結果

| 変数名 | 値 | this のアドレス | c_str() が返却する文字列のアドレス |
| --- | --- | --- | --- |
| s | AA ... | 0x7fff262f0710 | 0xddceb0 |
| s | AA ... | 0x7fff262f0710 | 0x7fff262f0720 |
| ss | AA ... | 0x7fff262f0730 | 0xddceb0 |

### case2 の結果

| 変数名 | 値 | this のアドレス | c_str() が返却する文字列のアドレス |
| --- | --- | --- | --- |
| s | AA ... | 0x7fff262f0710 | 0xddceb0 |
| ss | AA ... | 0x7fff262f0730 | 0x7fff262f0740 |
| s | AA ... | 0x7fff262f0710 | 0x7fff262f0720 |
| ss | AA ... | 0x7fff262f0730 | 0xddceb0 |

### この結果から言えることは

いや〜、涙が出そうです。
文字列が数バイトの場合は、いきなりヒープに確保せず、std::stringのメモリ構成に数バイトの文字列バッファを予め用意してそこに文字列を割り当てる。そして、文字数が一定数を超えるとヒープに確保するんですね。で、move semantics で破壊された側は予め用意したバッファを文字列バッファとすると。メモリアロケーションは処理自体の重さとCPUキャッシュの効率を考えると、細かなメモリアロケーションは劇的に速度的に低下しますもんね。
先人方、諸先輩方の努力でメモリアロケーションの効率化がなされているなんて、知りませんでした。。。
本当に脱帽です。。。
小さな配慮が大きな結果を出すんだなぁ〜と（目に見えにくいけど）、改めて思った。
そういえば、大昔、memcpyがショボい頃、アセンブラでスタックポインタ切り替えてcopyするという、今やったら爆発しそうなコード書いていたけど、その頃の速度に対する情熱が最近薄れているなぁ〜　（push / pop はステータスレジスタが変化しないので低クロックでキャッシュ機構も無いようなCPUだったので当時は早かったが、今はどうだろか？　あ、機種依存だけどDMAもお借りしたことあったな。。。）

## 戒め

親の心子知らず