List of Benchmark - text.Baldanders.info
tag:text.Baldanders.info,2020-10-03:/tags
2020-10-03T19:27:34+09:00
帰ってきた「しっぽのさきっちょ」
https://text.baldanders.info/images/avatar.jpg
https://text.baldanders.info/images/avatar.jpg
バイト列の同値性(『プログラミング言語 Go』読書会より)
tag:text.Baldanders.info,2020-10-03:/golang/equality-of-byte-arrays/
2020-10-03T10:27:34+00:00
2021-12-04T02:40:05+00:00
色んな方法で比較してみる
Spiegel
https://baldanders.info/profile/
<p>「<a href="https://gpl-reading.connpass.com/event/188380/">第5回『プログラミング言語Go』オンライン読書会</a>」に参加してみたです。</p>
<p>以前 Discord で某エベントに参加したことがあったけど,やっぱオンラインイベントはしんどい。
それでもメインで喋る人が決まってるから,今回は楽なほうだったかな。
Discord にしろ Zoom にしろ,オンラインでフリートーク・イベントはかなり難しいと思うのだが,みんなどうしてるんだろう。</p>
<p><ruby><rb>閑話休題</rb><rp> (</rp><rt>それはさておき</rt><rp>) </rp></ruby>。</p>
<p>今回,のっけから面白い話を聞いた。
『<a href="https://www.amazon.co.jp/dp/4621300253?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語 Go</a>』4.2章「スライス」に書かれている</p>
<figure>
<blockquote><q>配列と異なりスライスは比較可能ではありませんので,二つのスライスが同じ要素で構成されているかを検査するために <code>==</code> は使えません。標準ライブラリは二つのバイトスライス(<code>[]byte</code>)を比較するために<strong>高度に最適化</strong>された <code>bytes.Equal</code> 関数を提供しています。しかし…</q></blockquote>
<figcaption><div><q><a href="https://www.amazon.co.jp/dp/4621300253?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語 Go</a></q>より</div></figcaption>
</figure>
<p>という部分(強調は私がやりました)。
実際にコードを見てみると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Equal reports whether a and b
</span></span></span><span class="line"><span class="cl"><span class="c1">// are the same length and contain the same bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1">// A nil argument is equivalent to an empty slice.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Equal</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Neither cmd/compile nor gccgo allocates for these string conversions.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nb">string</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="o">==</span> <span class="nb">string</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>てなことになっている。
で,「これのどこが『高度に最適化』なん?」という話があったらしい。</p>
<p>バージョンを遡ってみると 1.12.7 までは</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Equal returns a boolean reporting whether a and b
</span></span></span><span class="line"><span class="cl"><span class="c1">// are the same length and contain the same bytes.
</span></span></span><span class="line"><span class="cl"><span class="c1">// A nil argument is equivalent to an empty slice.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Equal</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">bytealg</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>となっていた。
ちなみに <code>internal/bytealg</code> は内部パッケージで,中身はほぼ(アーキテクチャ毎に)アセンブラで記述されている。
実は <a href="https://go.dev/">Go</a> 1.13 では <code>string</code> 周りが大幅に強化されていて,その辺の影響が出ているのかもしれない。</p>
<p>それなら,どの程度のパフォーマンスか気になるよね。
というわけで,こんなコードを用意してみた<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">eq</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bytes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">hello1</span> <span class="p">=</span> <span class="s">"I could tell you my pass phrase, but then I would have to kill you."</span>
</span></span><span class="line"><span class="cl"> <span class="nx">hello2</span> <span class="p">=</span> <span class="s">"I could tell you my pass phrase, but then I would have to kill you."</span>
</span></span><span class="line"><span class="cl"> <span class="nx">helloA1</span> <span class="p">=</span> <span class="p">[</span><span class="mi">67</span><span class="p">]</span><span class="kt">byte</span><span class="p">{</span><span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x63</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x6d</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x2c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x6e</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x77</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x76</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x6b</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x2e</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">helloA2</span> <span class="p">=</span> <span class="p">[</span><span class="mi">67</span><span class="p">]</span><span class="kt">byte</span><span class="p">{</span><span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x63</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x6d</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x2c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x6e</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x77</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x76</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x6b</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x2e</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">helloS1</span> <span class="p">=</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">{</span><span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x63</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x6d</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x2c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x6e</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x77</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x76</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x6b</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x2e</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">helloS2</span> <span class="p">=</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">{</span><span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x63</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x6d</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x72</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x2c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x6e</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x77</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x64</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x61</span><span class="p">,</span> <span class="mh">0x76</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x6b</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x6c</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x79</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0x2e</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkByteEqual1</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">hello1</span> <span class="o">!=</span> <span class="nx">hello2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"false"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkByteEqual2</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">bytes</span><span class="p">.</span><span class="nf">Equal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">hello1</span><span class="p">),</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">hello2</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"false"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkByteEqual3</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">helloA1</span> <span class="o">!=</span> <span class="nx">helloA2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"false"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkByteEqual4</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">string</span><span class="p">(</span><span class="nx">helloA1</span><span class="p">[:])</span> <span class="o">!=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">helloA2</span><span class="p">[:])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"false"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkByteEqual5</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">bytes</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">helloA1</span><span class="p">[:],</span> <span class="nx">helloA2</span><span class="p">[:])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"false"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkByteEqual6</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">string</span><span class="p">(</span><span class="nx">helloS1</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">helloS2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"false"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkByteEqual7</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">bytes</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">helloS1</span><span class="p">,</span> <span class="nx">helloS2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"false"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>hello1</code>, <code>hello2</code> は <code>string</code> 型,<code>helloA1</code>, <code>helloA2</code> は <code>byte</code> 配列,<code>helloS1</code>, <code>helloS2</code> は <code>[]byte</code> 型で中身はみんな同じ。
これらを使って同値性(equallity)をチェックするのだが,内訳はこんな感じ。</p>
<table>
<thead>
<tr>
<th>関数名</th>
<th>比較手順</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>BenchmarkByteEqual1</code></td>
<td><code>string == string</code></td>
</tr>
<tr>
<td><code>BenchmarkByteEqual2</code></td>
<td><code>bytes.Equal([]byte(string), []byte(string))</code></td>
</tr>
<tr>
<td><code>BenchmarkByteEqual3</code></td>
<td><code><byte array> == <byte array></code></td>
</tr>
<tr>
<td><code>BenchmarkByteEqual4</code></td>
<td><code>string(<array>[:]) == string(<array>[:])</code></td>
</tr>
<tr>
<td><code>BenchmarkByteEqual5</code></td>
<td><code>bytes.Equal(<array>[:], <array>[:])</code></td>
</tr>
<tr>
<td><code>BenchmarkByteEqual6</code></td>
<td><code>string([]byte) == string([]byte)</code></td>
</tr>
<tr>
<td><code>BenchmarkByteEqual7</code></td>
<td><code>bytes.Equal([]byte, []byte)</code></td>
</tr>
</tbody>
</table>
<p>では,実際に動かしてみようか。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -bench ByteEqual -benchmem
</span></span><span class="line"><span class="cl">goos: linux
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: sample
</span></span><span class="line"><span class="cl">BenchmarkByteEqual1-4 301173402 4.04 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkByteEqual2-4 8552673 122 ns/op 160 B/op 2 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkByteEqual3-4 182939372 6.27 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkByteEqual4-4 191359716 6.29 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkByteEqual5-4 191511163 6.27 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkByteEqual6-4 168382664 7.16 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkByteEqual7-4 167058468 7.06 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok sample 12.140s
</span></span></code></pre></div><p>分かりやすく表にしておく。
ちなみに私の環境では 1ns 未満は誤差の範囲なのであしからず。</p>
<table>
<thead>
<tr>
<th>比較手順</th>
<th style="text-align:right">処理時間 (ns/op)</th>
<th style="text-align:right">Alloc 回数</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>string == string</code></td>
<td style="text-align:right">4.04</td>
<td style="text-align:right">0</td>
</tr>
<tr>
<td><code>bytes.Equal([]byte(string), []byte(string))</code></td>
<td style="text-align:right">122.00</td>
<td style="text-align:right">2</td>
</tr>
<tr>
<td><code><byte array> == <byte array></code></td>
<td style="text-align:right">6.27</td>
<td style="text-align:right">0</td>
</tr>
<tr>
<td><code>string(<array>[:]) == string(array>[:])</code></td>
<td style="text-align:right">6.29</td>
<td style="text-align:right">0</td>
</tr>
<tr>
<td><code>bytes.Equal(array>[:], <array>[:])</code></td>
<td style="text-align:right">6.27</td>
<td style="text-align:right">0</td>
</tr>
<tr>
<td><code>string([]byte) == string([]byte)</code></td>
<td style="text-align:right">7.16</td>
<td style="text-align:right">0</td>
</tr>
<tr>
<td><code>bytes.Equal([]byte, []byte)</code></td>
<td style="text-align:right">7.06</td>
<td style="text-align:right">0</td>
</tr>
</tbody>
</table>
<p><code>string</code> → <code>[]byte</code> へのキャスト時にアロケーションが発生している点に注意。</p>
<p>つか <code>string</code> 同士の比較処理が速いな! 配列と slice で若干差が出るのは仕方ないが,元が同じ型なら殆ど差がないようだ。
まぁ,これなら確かに</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Equal</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span> <span class="k">return</span> <span class="nb">string</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="o">==</span> <span class="nb">string</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span> <span class="p">}</span>
</span></span></code></pre></div><p>でもいっか,って気になるよな。</p>
<h2>ブックマーク</h2>
<ul>
<li>
<p><a href="https://greentown.tokyo/programminglanguage-go5/">第5回「プログラミング言語Go」オンライン読書会 - Mikke’s blog</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/join-strings-2/">【改訂版】文字列連結はどれが速い?</a></p>
</li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>ちなみに “I could tell you my pass phrase, but then I would have to kill you.” という物騒なフレーズは Simson Garfinkel 氏の『<a href="https://www.amazon.co.jp/exec/obidos/ASIN/4900900028/baldandersinf-22/" title="Amazon | PGP―暗号メールと電子署名 | シムソン ガーフィンケル, Simson Garfinkel, ユニテック 通販">PGP</a>』に載っていたパスフレーズの事例である(笑) <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
strings.EqualFold 関数を使え
tag:text.Baldanders.info,2019-12-15:/golang/use-equalfold-function/
2019-12-15T09:01:19+00:00
2021-12-04T02:40:05+00:00
大文字小文字を無視した文字列比較では素直に strings.EqualFold() 関数を使いましょう。
Spiegel
https://baldanders.info/profile/
<p><a href="https://golangci.com/" title="Automated code review for Go">GolangCI</a> が吐く<a href="https://text.baldanders.info/golang/code-review-with-golangci/" title="GolangCI でコード・レビューを自動化する">レビュー結果</a>を基にチマチマとコードを直していたのだが,その中で</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">SA6005: should use strings.EqualFold(a, b) instead of strings.ToLower(a) == strings.ToLower(b)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">if strings.ToLower(left) == strings.ToLower(right) {
</span></span></code></pre></div><p>という指摘があった。
いや,もの知らずでゴメンペコン。</p>
<p><a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.EqualFold()</code> 関数ってなんじゃら? と思ってソースコードを見たら</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// EqualFold reports whether s and t, interpreted as UTF-8 strings,
</span></span></span><span class="line"><span class="cl"><span class="c1">// are equal under Unicode case-folding.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">EqualFold</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="nx">t</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>と書かれている。</p>
<p>ふむふむ。
では試してみよう。
こんな感じのコードを書いて</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strings"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">lefts</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"go"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rights</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"Go"</span><span class="p">,</span> <span class="s">"GO"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">,</span> <span class="s">"Go"</span><span class="p">,</span> <span class="s">"GO"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">left</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lefts</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">right</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">rights</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"%s == %s : %v\n"</span><span class="p">,</span> <span class="nx">left</span><span class="p">,</span> <span class="nx">right</span><span class="p">,</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">EqualFold</span><span class="p">(</span><span class="nx">left</span><span class="p">,</span> <span class="nx">right</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>実行してみると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run sample1.go
</span></span><span class="line"><span class="cl">go == Go : true
</span></span><span class="line"><span class="cl">go == GO : true
</span></span><span class="line"><span class="cl">go == go : true
</span></span><span class="line"><span class="cl">go == Go : false
</span></span><span class="line"><span class="cl">go == GO : false
</span></span><span class="line"><span class="cl">go == go : false
</span></span><span class="line"><span class="cl">go == Go : false
</span></span><span class="line"><span class="cl">go == GO : false
</span></span><span class="line"><span class="cl">go == go : false
</span></span><span class="line"><span class="cl">go == Go : true
</span></span><span class="line"><span class="cl">go == GO : true
</span></span><span class="line"><span class="cl">go == go : true
</span></span></code></pre></div><p>ってな感じになった。
全角と半角は区別してくれるらしい。
Unicode の文字種をきちんと判別しているということだ。</p>
<p>ちなみに <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.ToLower()</code> 関数を使って</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strings"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">lefts</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"go"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rights</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"Go"</span><span class="p">,</span> <span class="s">"GO"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">,</span> <span class="s">"Go"</span><span class="p">,</span> <span class="s">"GO"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">left</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lefts</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">right</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">rights</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"%s == %s : %v\n"</span><span class="p">,</span> <span class="nx">left</span><span class="p">,</span> <span class="nx">right</span><span class="p">,</span> <span class="p">(</span><span class="nx">left</span> <span class="o">==</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">ToLower</span><span class="p">(</span><span class="nx">right</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>とやっても同じ結果になる。</p>
<p><a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.EqualFold()</code> 関数と <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.ToLower()</code> 関数でどっちが速いかなんてのは考えるまでもないのだが,いちおう試しておこう。
こんな感じのコードでいいかな。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">equalfold</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strings"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">lefts</span> <span class="p">=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"go"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rights</span> <span class="p">=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"Go"</span><span class="p">,</span> <span class="s">"GO"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">,</span> <span class="s">"Go"</span><span class="p">,</span> <span class="s">"GO"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rights2</span> <span class="p">=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"go"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">,</span> <span class="s">"go"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkEqualCase</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">left</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lefts</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">right</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">rights2</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">if</span> <span class="nx">left</span> <span class="o">==</span> <span class="nx">right</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">left</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">right</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkEqualLower</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">left</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lefts</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">right</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">rights</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">if</span> <span class="nx">left</span> <span class="o">==</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">ToLower</span><span class="p">(</span><span class="nx">right</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">left</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">right</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkEqualFold</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">left</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lefts</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">right</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">rights</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">if</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">EqualFold</span><span class="p">(</span><span class="nx">left</span><span class="p">,</span> <span class="nx">right</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">left</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nx">right</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>BenchmarkEqualCase</code> は他の2つのコードとの比較用に書いてみた。
実行結果はこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -bench Equal -benchmem
</span></span><span class="line"><span class="cl">goos: linux
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: sample
</span></span><span class="line"><span class="cl">BenchmarkEqualCase-4 32061360 36.2 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkEqualLower-4 1367802 869 ns/op 64 B/op 8 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkEqualFold-4 3149362 378 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok sample 4.748s
</span></span></code></pre></div><p>表にまとめておこう。</p>
<table>
<thead>
<tr>
<th>関数名</th>
<th style="text-align:right">実行時間</th>
<th style="text-align:right">Alloc サイズ</th>
<th style="text-align:right">Alloc 回数</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>BenchmarkEqualCase</code></td>
<td style="text-align:right">36.2 ns</td>
<td style="text-align:right">0 bytes</td>
<td style="text-align:right">0</td>
</tr>
<tr>
<td><code>BenchmarkEqualLower</code></td>
<td style="text-align:right">869 ns</td>
<td style="text-align:right">64 bytes</td>
<td style="text-align:right">8</td>
</tr>
<tr>
<td><code>BenchmarkEqualFold</code></td>
<td style="text-align:right">378 ns</td>
<td style="text-align:right">0 bytes</td>
<td style="text-align:right">0</td>
</tr>
</tbody>
</table>
<p><code>BenchmarkEqualCase</code> と <code>BenchmarkEqualFold</code> の比較では <code>BenchmarkEqualFold</code> のほうが10倍の時間がかかっているが,それよりも <code>BenchmarkEqualLower</code> の処理のほうが圧倒的に遅いことが分かる。
まぁメモリ・アロケーションが絡むとねぇ。</p>
<p>というわけで,大文字小文字を無視した文字列比較では素直に <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.EqualFold()</code> 関数を使いましょう,という話でした。</p>
<h2>【付録】 “NUL” 文字の比較</h2>
<p>まるきし余談ではあるが</p>
<ul>
<li><a href="https://mattn.kaoriya.net/software/lang/go/20190806152526.htm">Big Sky :: Go で大文字小文字無視の文字列比較ベンチマーク</a></li>
</ul>
<p>この記事にある <code>isDevNull3</code> 関数について</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">isDevNull3</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">ToLower</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="o">==</span> <span class="s">"nul"</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.EqualFold()</code> 関数を使うよう書き換えてみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">isDevNull3</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">return</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">EqualFold</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="s">"nul"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これでベンチマークテストを実行すると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">goos: linux
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: sample/lowercase
</span></span><span class="line"><span class="cl">BenchmarkS1-4 41640913 27.2 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkS2-4 35464141 30.7 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkS3-4 12628962 94.4 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok sample/lowercase 3.578s
</span></span></code></pre></div><p>メモリ・アロケーションが発生しなくなり,かなり速くなる。
まぁ,それでもいっちゃん遅いのだが(笑)</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
【改訂版】文字列連結はどれが速い?
tag:text.Baldanders.info,2019-10-13:/golang/join-strings-2/
2019-10-13T08:15:36+00:00
2021-12-04T02:40:05+00:00
つまり []byte 配列への append() と strings.Builder への追記と strings.Join() は実質的に同じ処理と言える。
Spiegel
https://baldanders.info/profile/
<p>今回も小ネタでお送りしております。</p>
<p>2015年に「<a href="https://text.baldanders.info/golang/join-strings/">文字列連結はどれが速い?</a>」という記事を書いた。
あれから文字連結に関してどう変わったのか。
特に <a href="https://go.dev/">Go</a> 1.10 で <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> が追加されているので,その辺も含めて再検証してみる。</p>
<p>今回検証するコードは以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">join</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bytes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strings"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">sz8k</span> <span class="p">=</span> <span class="mi">8</span> <span class="o">*</span> <span class="mi">1024</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">JoinStringPlus</span><span class="p">(</span><span class="nx">ss</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">str</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ss</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">str</span> <span class="o">+=</span> <span class="nx">s</span> <span class="o">+</span> <span class="s">"\n"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">JoinStringJoin</span><span class="p">(</span><span class="nx">ss</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">strings</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="nx">ss</span><span class="p">,</span> <span class="s">"\n"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">JoinStringByteAppend</span><span class="p">(</span><span class="nx">ss</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ss</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="nx">s</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="s">"\n"</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">JoinStringByteAppend8K</span><span class="p">(</span><span class="nx">ss</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">sz8k</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ss</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="nx">s</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="s">"\n"</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">JoinStringBuilder</span><span class="p">(</span><span class="nx">ss</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">strings</span><span class="p">.</span><span class="nx">Builder</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ss</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="s">"\n"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">JoinStringBuilder8K</span><span class="p">(</span><span class="nx">ss</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">strings</span><span class="p">.</span><span class="nx">Builder</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">Grow</span><span class="p">(</span><span class="nx">sz8k</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ss</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="s">"\n"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">JoinStringBuffer</span><span class="p">(</span><span class="nx">ss</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ss</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="s">"\n"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">JoinStringBuffer8K</span><span class="p">(</span><span class="nx">ss</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewBuffer</span><span class="p">(</span><span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">sz8k</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">ss</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="s">"\n"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>各関数の内容は以下の通り。</p>
<table>
<thead>
<tr>
<th>関数名</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>JoinStringPlus</code></td>
<td><code>+</code> 演算子で連結する</td>
</tr>
<tr>
<td><code>JoinStringJoin</code></td>
<td><a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Join</code> 関数で連結する</td>
</tr>
<tr>
<td><code>JoinStringByteAppend</code></td>
<td><code>[]byte</code> 配列に追記する</td>
</tr>
<tr>
<td><code>JoinStringByteAppend8K</code></td>
<td><code>[]byte</code> 配列に追記する(8KB アロケーション)</td>
</tr>
<tr>
<td><code>JoinStringBuilder</code></td>
<td><a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> に追記する</td>
</tr>
<tr>
<td><code>JoinStringBuilder8K</code></td>
<td><a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> に追記する(8KB アロケーション)</td>
</tr>
<tr>
<td><code>JoinStringBuffer</code></td>
<td><a href="https://golang.org/pkg/bytes/" title="bytes - The Go Programming Language"><code>bytes</code></a><code>.Buffer</code> に追記する</td>
</tr>
<tr>
<td><code>JoinStringBuffer8K</code></td>
<td><a href="https://golang.org/pkg/bytes/" title="bytes - The Go Programming Language"><code>bytes</code></a><code>.Buffer</code> に追記する(8KB アロケーション)</td>
</tr>
</tbody>
</table>
<p>使うメソッドによって出力する型が異なるが(<code>string</code> or <code>[]byte</code>),今回は無視することにした<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<p>ベンチマーク用のコードは以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">join</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bufio"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ReadAll</span><span class="p">(</span><span class="nx">path</span> <span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">file</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span> <span class="c1">//maybe file path
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">file</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">scanner</span> <span class="o">:=</span> <span class="nx">bufio</span><span class="p">.</span><span class="nf">NewScanner</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">scanner</span><span class="p">.</span><span class="nf">Scan</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">list</span><span class="p">,</span> <span class="nx">scanner</span><span class="p">.</span><span class="nf">Text</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">scanner</span><span class="p">.</span><span class="nf">Err</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">list</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">content</span> <span class="p">=</span> <span class="nf">ReadAll</span><span class="p">(</span><span class="s">"CollisionsForHashFunctions.txt"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkJoinStringPlus</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">JoinStringPlus</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkJoinStringJoin</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">JoinStringJoin</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkJoinStringByteAppend</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">JoinStringByteAppend</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkJoinStringByteAppend8K</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">JoinStringByteAppend8K</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkJoinStringBuilder</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">JoinStringBuilder</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkJoinStringBuilder8K</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">JoinStringBuilder8K</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkJoinStringBuffer</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">JoinStringBuffer</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkJoinStringBuffer8K</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">JoinStringBuffer8K</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>入力テキストは<a href="https://text.baldanders.info/golang/join-strings/" title="文字列連結はどれが速い?">前回</a>と同じ <a href="https://baldanders.info/spiegel/archive/CollisionsForHashFunctions.txt"><code>CollisionsForHashFunctions.txt</code></a> を使用した。
8KB ほどのサイズがある。
つまりコピー先バッファに 8KB の容量があれば追加のアロケーションは発生しないことになる。</p>
<p>では,さっそく実行してみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -bench JoinString -benchmem
</span></span><span class="line"><span class="cl">goos: linux
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: join
</span></span><span class="line"><span class="cl">BenchmarkJoinStringPlus-4 19484 65256 ns/op 272160 B/op 69 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkJoinStringJoin-4 371649 3087 ns/op 8192 B/op 1 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkJoinStringByteAppend-4 151417 8339 ns/op 35376 B/op 12 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkJoinStringByteAppend8K-4 502942 2544 ns/op 8192 B/op 1 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkJoinStringBuilder-4 130408 8434 ns/op 35376 B/op 12 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkJoinStringBuilder8K-4 418900 2781 ns/op 8192 B/op 1 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkJoinStringBuffer-4 133052 9545 ns/op 32240 B/op 8 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkJoinStringBuffer8K-4 351681 3526 ns/op 8192 B/op 1 allocs/op
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok join 12.695s
</span></span></code></pre></div><p>結果を表にまとめておこう。</p>
<table>
<thead>
<tr>
<th>関数名</th>
<th style="text-align:right">実行時間</th>
<th style="text-align:right">Alloc サイズ</th>
<th style="text-align:right">Alloc 回数</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>JoinStringPlus</code></td>
<td style="text-align:right">65,256 ns</td>
<td style="text-align:right">2,702,160 bytes</td>
<td style="text-align:right">69</td>
</tr>
<tr>
<td><code>JoinStringJoin</code></td>
<td style="text-align:right">3,087 ns</td>
<td style="text-align:right">8,192 bytes</td>
<td style="text-align:right">1</td>
</tr>
<tr>
<td><code>JoinStringByteAppend</code></td>
<td style="text-align:right">8,339 ns</td>
<td style="text-align:right">35,376 bytes</td>
<td style="text-align:right">12</td>
</tr>
<tr>
<td><code>JoinStringByteAppend8K</code></td>
<td style="text-align:right">2,544 ns</td>
<td style="text-align:right">8192 bytes</td>
<td style="text-align:right">1</td>
</tr>
<tr>
<td><code>JoinStringBuilder</code></td>
<td style="text-align:right">8,434 ns</td>
<td style="text-align:right">35,376 bytes</td>
<td style="text-align:right">12</td>
</tr>
<tr>
<td><code>JoinStringBuilder8K</code></td>
<td style="text-align:right">2,781 ns</td>
<td style="text-align:right">8,192 bytes</td>
<td style="text-align:right">1</td>
</tr>
<tr>
<td><code>JoinStringBuffer</code></td>
<td style="text-align:right">9,545 ns</td>
<td style="text-align:right">32,240 bytes</td>
<td style="text-align:right">8</td>
</tr>
<tr>
<td><code>JoinStringBuffer8K</code></td>
<td style="text-align:right">3,526 ns</td>
<td style="text-align:right">8192 bytes</td>
<td style="text-align:right">1</td>
</tr>
</tbody>
</table>
<p>まず <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Join()</code> 関数を使った連結がめっさ速くなってアロケーション回数も1回のみになっていることにビックリした。
ソースコードを見てみたら,やっぱり <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Join()</code> 関数内部で <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> を使っていた。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Join concatenates the elements of a to create a single string. The separator string
</span></span></span><span class="line"><span class="cl"><span class="c1">// sep is placed between elements in the resulting string.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Join</span><span class="p">(</span><span class="nx">a</span> <span class="p">[]</span><span class="kt">string</span><span class="p">,</span> <span class="nx">sep</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">a</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">sep</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">n</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">b</span> <span class="nx">Builder</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">Grow</span><span class="p">(</span><span class="nx">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">a</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">a</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">sep</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">b</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ちなみに <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> への追記処理は以下のようになっている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// WriteString appends the contents of s to b's buffer.
</span></span></span><span class="line"><span class="cl"><span class="c1">// It returns the length of s and a nil error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">Builder</span><span class="p">)</span> <span class="nf">WriteString</span><span class="p">(</span><span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">copyCheck</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nx">buf</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">b</span><span class="p">.</span><span class="nx">buf</span><span class="p">,</span> <span class="nx">s</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">),</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>つまり <code>[]byte</code> 配列への <code>append()</code> と <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> への追記と <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Join()</code> は実質的に同じ処理で,それぞれの前処理分だけ差が出ているということになる。</p>
<p>今回の検証では</p>
<ol>
<li>やっぱり <code>+</code> 演算子による連結はダメダメ<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li>よほどの最適化が要求されない限り <code>[]byte</code> 配列への <code>append()</code> は <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> へ代替可能<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></li>
<li><a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Join()</code> 関数のパフォーマンスは十分なので気軽に使ってよい</li>
<li>文字列連結に限るなら,もはや <a href="https://golang.org/pkg/bytes/" title="bytes - The Go Programming Language"><code>bytes</code></a><code>.Buffer</code> は有利とは言えない</li>
</ol>
<p>といったところだろうか。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><code>string</code> 型は不変オブジェクトなので,通常は <code>[]byte</code> 型との相互変換の際にメモリ・アロケーションとデータ・コピーが発生する。ちなみに <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> の <code>String()</code> メソッドでは <a href="https://golang.org/pkg/unsafe/" title="unsafe - The Go Programming Language"><code>unsafe</code></a> パッケージを使って無理やりキャスティングしている。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>リテラル文字列同士の連結はコンパイラが処理するので <code>+</code> 演算子で無問題。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p>内部で <code>append()</code> 関数を使っていることから分かる通り <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Builder</code> のインスタンスはコピーして使えないので注意が必要である(インスタンスのポインタを渡せばOK)。当然ながら goroutine-safe ではないので複数の goroutine 間で共有できない。 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
テスト・フレームワークで Project Euler を解く
tag:text.Baldanders.info,2018-03-26:/golang/testing-for-project-euler/
2018-03-26T10:27:50+00:00
2021-08-12T21:22:05+00:00
ベンチマーク・テストもできるよ。
Spiegel
https://baldanders.info/profile/
<p>別のセクションで「<a href="https://text.baldanders.info/remark/2018/03/project-euler/">Project Euler で遊ぶ</a>」話を書いたのだが,この記事のコメントで「ユニットテストを書く練習になる」というのがあって「なるほど!」と膝を打った。
つか,自力で気づけよ,自分。
まだまだ TDD が身につかないなぁ。</p>
<p>ちうわけで,以下の問題(Problem)をテスト・フレームワークで解いてみる。</p>
<figure lang="en">
<blockquote>
<q>If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.<br>
Find the sum of all the multiples of 3 or 5 below 1000.</q>
</blockquote>
<figcaption><div>via <q><a href="https://projecteuler.net/problem=1">Problem 1 - Project Euler</a></q></div></figcaption>
</figure>
<figure>
<blockquote>
<q>10未満の自然数のうち, 3 もしくは 5 の倍数になっているものは 3, 5, 6, 9 の4つがあり, これらの合計は 23 になる.<br>
同じようにして, 1000 未満の 3 か 5 の倍数になっている数字の合計を求めよ.</q>
</blockquote>
<figcaption><div><q><a href="http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%201">Problem 1 - PukiWiki</a></q>より</div></figcaption>
</figure>
<p>まず,ベタに解いたコード。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Answer0 returns answer to this problem
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">max</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">n</span> <span class="o">:=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">n</span> <span class="p"><</span> <span class="nx">max</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">n</span><span class="o">%</span><span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="p">(</span><span class="nx">n</span><span class="o">%</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">n</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">sum</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これをテストにかける。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">problem1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAnswer</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span><span class="p">,</span> <span class="nx">answer</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">23</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tc</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a</span> <span class="o">:=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer0(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>実行すると以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v .
</span></span><span class="line"><span class="cl">=== RUN TestAnswer
</span></span><span class="line"><span class="cl">--- PASS: TestAnswer (0.00s)
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok project-euler/problem-1 0.128s
</span></span></code></pre></div><p>これで最初の例示について正しい答えを出していることがわかる。
では1,000未満についてもやってみようか。
以下のようにテスト項目を追加する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAnswer</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span> <span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span><span class="p">,</span> <span class="nx">answer</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">23</span><span class="p">},</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span> <span class="p">,</span> <span class="nx">tc</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a</span> <span class="o">:=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer0(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>まだ正解が分からないので,とりあえず <code>0</code> をぶちこんでいる。
これを実行する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v .
</span></span><span class="line"><span class="cl">=== RUN TestAnswer
</span></span><span class="line"><span class="cl">--- FAIL: TestAnswer (0.00s)
</span></span><span class="line"><span class="cl"> answer_test.go:16: Answer0(1000) = ******, want 0.
</span></span><span class="line"><span class="cl">FAIL
</span></span><span class="line"><span class="cl">FAIL project-euler/problem-1 0.106s
</span></span><span class="line"><span class="cl">exit status 1
</span></span></code></pre></div><p>テストは当然失敗するが,回答が表示されるので<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> これを <a href="https://projecteuler.net/">Project Euler</a> のサイトで入力してチェックする。
正解が分かったら改めて正解をテストにセットしなおす。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAnswer</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span> <span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span><span class="p">,</span> <span class="nx">answer</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">23</span><span class="p">},</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="o">******</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span> <span class="p">,</span> <span class="nx">tc</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a</span> <span class="o">:=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer0(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>これで再テストすると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -v .
</span></span><span class="line"><span class="cl">=== RUN TestAnswer
</span></span><span class="line"><span class="cl">--- PASS: TestAnswer (0.00s)
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok project-euler/problem-1 0.128s
</span></span></code></pre></div><p>ちゃんと PASS していることが確認できた。</p>
<h2>改良版</h2>
<p>ここで解説を参考に改良版のコードを組む。
こんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Answer1 returns answer to this problem (refactoring version)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Answer1</span><span class="p">(</span><span class="nx">max</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">sumDivisibleBy</span><span class="p">(</span><span class="nx">max</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="nf">sumDivisibleBy</span><span class="p">(</span><span class="nx">max</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="o">-</span> <span class="nf">sumDivisibleBy</span><span class="p">(</span><span class="nx">max</span><span class="p">,</span> <span class="mi">3</span><span class="o">*</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">sumDivisibleBy</span><span class="p">(</span><span class="nx">max</span><span class="p">,</span> <span class="nx">n</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">m</span> <span class="o">:=</span> <span class="p">(</span><span class="nx">max</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="nx">n</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">n</span> <span class="o">*</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span> <span class="p">(</span><span class="nx">m</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span> <span class="o">/</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>このコードも同じテストにかける。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestAnswer</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span> <span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">max</span><span class="p">,</span> <span class="nx">answer</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="mi">23</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">max</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nx">answer</span><span class="p">:</span> <span class="o">******</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span> <span class="p">,</span> <span class="nx">tc</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">a</span> <span class="o">:=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer0(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">a</span> <span class="p">=</span> <span class="nf">Answer1</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Answer1(%v) = %v, want %v."</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span> <span class="nx">a</span><span class="p">,</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">answer</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>全てのテストケースでパスすればOK。</p>
<h2>ベンチマーク・テスト</h2>
<p>更に <code>Answer0()</code>, <code>Answer1()</code> 両関数をベンチマーク・テストで比較してみることにする。
ベンチマーク・テスト用のコードは以下の通り<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkAnswer0</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nf">Answer0</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkAnswer1</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nf">Answer1</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ではベンチマーク・テストを実行してみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -bench . -benchmem
</span></span><span class="line"><span class="cl">goos: windows
</span></span><span class="line"><span class="cl">goarch: amd64
</span></span><span class="line"><span class="cl">pkg: project-euler/problem-1
</span></span><span class="line"><span class="cl">BenchmarkAnswer0-4 500000 2415 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkAnswer1-4 2000000000 0.29 ns/op 0 B/op 0 allocs/op
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok project-euler/problem-1 1.952s
</span></span></code></pre></div><p>おおっ。
万倍ちがうぜ(笑)</p>
<p>このようにテストフレームワークを使って楽しく <a href="https://projecteuler.net/">Project Euler</a> で遊ぶことができる。
テストを書く習慣づけになればもっといいんだろうけど。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://text.baldanders.info/golang/testing/">Go 言語のテスト・フレームワーク</a></li>
<li><a href="https://text.baldanders.info/golang/join-strings/">文字列連結はどれが速い?</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>一応ネタバレ防止用に伏字にしている。ご容赦。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>ベンチマーク用のコードは <code>***_test.go</code> ファイルに含めること。また関数名は <code>Benchmark</code> から始まる名前にする。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
「ズンドコチェック」なるものが流行っているらしい
tag:text.Baldanders.info,2016-03-12:/golang/zundoko-choir/
2016-03-12T13:12:53+00:00
2021-08-12T21:22:05+00:00
というわけで Go 言語で実装することを考えてみる。
Spiegel
https://baldanders.info/profile/
<figure style='margin:0 auto;text-align:center;'>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから<br>「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」って出力した後終了って関数作ったら満点で単位貰ってた</p>— てくも (@kumiromilk) <a href="https://twitter.com/kumiromilk/status/707437861881180160">2016年3月9日</a></blockquote>
</figure>
<p>「習作(study)」としてはなかなか秀逸なアイデアだと思う。
これで満点くれる教官も流石だが(笑) 巷では「ズンドコキヨシ」とか「キヨシチェック」とか「ズンドコチェック」とか呼ばれているらしい。</p>
<p>というわけで</p>
<blockquote>
<p>「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」って出力した後終了って関数</p>
</blockquote>
<p>を <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で実装することを考えてみる。
私はコレを「ズンドコ・コール(zundoko-choir)」と呼ぶことにする。</p>
<p>とはいえ,ズンドコ・コールを実装する事自体はそう難しくない。
たとえばこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math/rand"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zun</span> <span class="p">=</span> <span class="s">"ズン"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">doko</span> <span class="p">=</span> <span class="s">"ドコ"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">kiyoshi</span> <span class="p">=</span> <span class="s">"キ・ヨ・シ!"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">generate</span><span class="p">()</span> <span class="kd">chan</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">zundoko</span> <span class="p">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span><span class="nx">zun</span><span class="p">,</span> <span class="nx">doko</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rand</span><span class="p">.</span><span class="nf">Seed</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o"><-</span> <span class="nx">zundoko</span><span class="p">[</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="mi">2</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ch</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zundoko</span> <span class="o">:=</span> <span class="nf">generate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zcount</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zd</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">zundoko</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="nx">zd</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">zd</span> <span class="o">==</span> <span class="nx">zun</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zcount</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nx">zcount</span> <span class="o">>=</span> <span class="mi">4</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zcount</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="nx">kiyoshi</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>「ズン」および「ドコ」をランダムに生成する部分は <a href="http://golang.org/ref/spec#Channel_types">channel</a> と <a href="http://golang.org/ref/spec#Go_statements">goroutine</a> を使えばいいだろう(<code>generate()</code> 関数内の処理)。
擬似乱数は厳密でなくてもいいので安直に <a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> を使うことにした<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。
さらに「ズン」「ズン」「ズン」「ズン」「ドコ」の配列パターンのチェックだが,「ズン」が4回以上連続で来た直後に「ドコ」が来たら OK としてみた。
まぁ,これがもっとも素朴な実装でパフォーマンスとしてもそれほど遜色ない筈。</p>
<p>と,ここまで考えてハタと気づいた。
問題は「自作関数を作り記述しなさい」なんだからメイン関数にロジック書いたらアカンやん!</p>
<p>というわけでまたもゴリゴリとコードを書いてパッケージにしてしまった。
アホだ,私(笑)</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/zundoko">spiegel-im-spiegel/zundoko: Zundoko-choirs</a></li>
</ul>
<p>出力は標準出力に直書きするのではなく <a href="http://golang.org/ref/spec#String_types">string</a> の <a href="http://golang.org/ref/spec#Slice_types">slice</a> に <code>append()</code> することで実現する。
この出力先を <code>Choirs</code> 型として定義した。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Choirs - zundoko-choirs list
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Choirs</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="p">[]</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Push is append choirs
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Choirs</span><span class="p">)</span> <span class="nf">Push</span><span class="p">(</span><span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nx">c</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">c</span><span class="p">,</span> <span class="nx">s</span><span class="p">)</span> <span class="c1">//maybe panic if c is nil.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Choirs</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">128</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">c</span><span class="p">.</span><span class="nx">c</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">content</span><span class="p">,</span> <span class="nx">s</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">string</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ちなみに文字列の連結は <a href="https://golang.org/pkg/strings/" title="strings - The Go Programming Language"><code>strings</code></a><code>.Join()</code> 関数は使わず「<a href="https://text.baldanders.info/golang/join-strings/" title="文字列連結はどれが速い? — プログラミング言語 Go">文字列連結はどれが速い?</a>」で紹介した方法を使っている。</p>
<p>これで最初のコードは</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">generate</span><span class="p">()</span> <span class="kd">chan</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">zd</span> <span class="p">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="kt">string</span><span class="p">{</span><span class="nx">Zun</span><span class="p">,</span> <span class="nx">Doko</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rand</span><span class="p">.</span><span class="nf">Seed</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ch</span> <span class="o"><-</span> <span class="nx">zd</span><span class="p">[</span><span class="nx">rand</span><span class="p">.</span><span class="nf">Intn</span><span class="p">(</span><span class="mi">2</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ch</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Run zundoko-choirs
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Run</span><span class="p">()</span> <span class="o">*</span><span class="nx">Choirs</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zd</span> <span class="o">:=</span> <span class="nf">generate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">Choirs</span><span class="p">{</span><span class="nb">make</span><span class="p">([]</span><span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zcount</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">zd</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Push</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">s</span> <span class="o">==</span> <span class="nx">Zun</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zcount</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nx">zcount</span> <span class="o">>=</span> <span class="mi">4</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zcount</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Push</span><span class="p">(</span><span class="nx">Kiyoshi</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">c</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>と書き換えることができる。
このパッケージを呼び出すメイン側は例えば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/zundoko"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nx">zundoko</span><span class="p">.</span><span class="nf">Run</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>と書けばいい。</p>
<p>ところで「ズン」「ドコ」の出力は <code>Choirs</code> 型で保持られているので,末尾の5要素のパターンを調べる別の方法もあると気づく。
たとえばこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">matchingPattern</span> <span class="p">=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="nx">Zun</span><span class="p">,</span> <span class="nx">Zun</span><span class="p">,</span> <span class="nx">Zun</span><span class="p">,</span> <span class="nx">Zun</span><span class="p">,</span> <span class="nx">Doko</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Choirs</span><span class="p">)</span> <span class="nf">match</span><span class="p">()</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">c</span><span class="p">)</span> <span class="p"><</span> <span class="mi">5</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">reflect</span><span class="p">.</span><span class="nf">DeepEqual</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">c</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">c</span><span class="p">)</span><span class="o">-</span><span class="mi">5</span><span class="p">:],</span> <span class="nx">matchingPattern</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>この関数を使えば <code>Run()</code> 関数は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Run2 zundoko-choirs (another logic)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Run2</span><span class="p">()</span> <span class="o">*</span><span class="nx">Choirs</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zd</span> <span class="o">:=</span> <span class="nf">generate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">Choirs</span><span class="p">{</span><span class="nb">make</span><span class="p">([]</span><span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">zd</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Push</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">match</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Push</span><span class="p">(</span><span class="nx">Kiyoshi</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">c</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>となり随分すっきりする。
ただこれコストが高くつきそうである。
というわけで,これも調べてみた。
まず以下のベンチマークを書く。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">zundoko</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRun1</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Run</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkRun2</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Run2</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>BenchmarkRun1</code> が従来のもの, <code>BenchmarkRun2</code> が先程の <code>match()</code> 関数を使ったバージョンである。
結果は以下のとおり。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go test -bench Run -benchmem
</span></span><span class="line"><span class="cl">testing: warning: no tests to run
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">BenchmarkRun1-4 50000 28141 ns/op 1800 B/op 9 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkRun2-4 30000 40102 ns/op 3912 B/op 115 allocs/op
</span></span><span class="line"><span class="cl">ok github.com/spiegel-im-spiegel/zundoko 4.261s
</span></span></code></pre></div><p>乱数の要素が絡むので毎回同じ値ではないが,傾向としてはこんな感じ。
<code>BenchmarkRun2</code> のほうが allocation 回数が圧倒的に多いのが分かるだろう。
これがスピードにもダイレクトに反映されている感じである。</p>
<p>今回は「「ズン」が4回以上連続で来た直後に「ドコ」が来たら OK」という単純なロジックだったが,もっと複雑なパターンが要求される場合は工夫が必要かもしれない<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<p>「ズン」と「ドコ」の出現回数を数える関数も作ってみた。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//CountZunDoko returns count of "ZUN" and "DOKO" choirs
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Choirs</span><span class="p">)</span> <span class="nf">CountZunDoko</span><span class="p">()</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">z</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="nx">d</span> <span class="o">:=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">c</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">z</span><span class="p">,</span> <span class="nx">d</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">s</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">c</span><span class="p">.</span><span class="nx">c</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">s</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">Zun</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">z</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">Doko</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">d</span><span class="o">++</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">z</span><span class="p">,</span> <span class="nx">d</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>たとえば <code>generate()</code> 関数内で使っている擬似乱数パッケージを別のものに換えた時に統計処理で簡単な性能評価ができるかもしれない。
今回はそこまではしなけど(擬似乱数の話はいずれやりたい)。</p>
<p>こうやって手遊びでコードを弄るのは楽しいものである。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://qiita.com/shunsugai@github/items/971a15461de29563bf90">ズンドコキヨシまとめ - Qiita</a></li>
<li><a href="http://qiita.com/shinderuman@github/items/2ff67c2404647d2b7ea6">ズンドコキヨシ with Go (n番煎じ) - Qiita</a></li>
<li><a href="http://qiita.com/Rompei/items/bfa03fbc9a94a37703bb">ズンドコキヨシGolang並列版 - Qiita</a> : 「ズン」「ドコ」の生成部分を CPU の数だけ並列処理で行わせてひとつの <a href="http://golang.org/ref/spec#Channel_types">channel</a> に入力するというユニークな実装</li>
<li><a href="http://qiita.com/spiegel-im-spiegel/items/6a5bc07dbfa46a328e26">「ズンドコキヨシ」と擬似乱数 - Qiita</a> : Qiita で擬似乱数について簡単にまとめてみた。整理できたらこちらでも記事にするかも</li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> の乱数生成アルゴリズムの既定は線形合同法らしい。<a href="http://www001.upp.so-net.ne.jp/isaku/rand.html" title="良い乱数・悪い乱数">線形合同法は性能が良くなく</a>ゲームや暗号等では使えない。<a href="https://golang.org/pkg/math/rand/" title="rand - The Go Programming Language"><code>math/rand</code></a> の乱数生成アルゴリズムは他のものに入れ替えることができる。たとえば <a href="https://github.com/seehuhn/mt19937" title="seehuhn/mt19937: An implementation of Takuji Nishimura's and Makoto Matsumoto's Mersenne Twister pseudo random number generator in Go."><code>seehuhn/mt19937</code></a> パッケージが使える。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>たとえば <a href="https://golang.org/pkg/container/list/" title="list - The Go Programming Language"><code>container/list</code></a> や <a href="https://golang.org/pkg/container/ring/" title="ring - The Go Programming Language"><code>container/ring</code></a> といったパッケージを使う手がある。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
文字列連結はどれが速い?
tag:text.Baldanders.info,2015-09-21:/golang/join-strings/
2015-09-21T14:08:07+00:00
2021-08-12T21:22:05+00:00
Go 言語で文字列の連結を行う際にどうやるのが一番速いか,という話。
Spiegel
https://baldanders.info/profile/
<p>(初出: <a href="http://qiita.com/spiegel-im-spiegel/items/16ab7dabbd0749281227">Golang の文字列連結はどちらが速い? - Qiita</a>)</p>
<p><a href="https://text.baldanders.info/golang/string-and-rune/" title="String と Rune">前回</a>につづき <a href="http://golang.org/ref/spec#String_types">string</a> の話題。
<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で文字列の連結を行う際にどうやるのが一番速いか,という話。</p>
<div class="box"><strong>【2019-10-13 追記】</strong> <a href="https://text.baldanders.info/golang/join-strings-2/">改訂版</a>を公開しました。</div>
<h2>文字列連結を行う4つの方法</h2>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で文字列の連結を行う際には概ね以下の4つの方法がある。</p>
<ol>
<li>“<code>+</code>” 演算子で連結する</li>
<li><a href="http://golang.org/pkg/strings/"><code>strings</code></a><code>.Join</code> で連結する</li>
<li><a href="http://golang.org/pkg/bytes/"><code>bytes</code></a><code>.Buffer</code> に追記する</li>
<li><code>[]byte</code> に <code>append</code> する</li>
</ol>
<p><a href="http://golang.org/ref/spec#String_types">string</a> は「不変(immutable)」なので,最初の2つが高コストになるだろうことはすぐに想像がつく。</p>
<ul>
<li><a href="http://qiita.com/ruiu/items/2bb83b29baeae2433a79">Goでは文字列連結はコストの高い操作 - Qiita</a></li>
<li><a href="http://atotto.hatenadiary.jp/entry/2013/04/26/202701">Go言語で効率良く文字列を連結する話 #golang - memoメモ</a></li>
</ul>
<p>では残りの2つはどうなのかというと</p>
<ul>
<li><a href="http://qiita.com/ono_matope/items/d5e70d8a9ff2b54d5c37">Goの文字列結合のパフォーマンス - Qiita</a></li>
</ul>
<p>によると最後のが一番速いらしい。ほんじゃまぁ,確かめてみるか。</p>
<h2>サンプルコードを用意</h2>
<p>以下のコード <code>join.go</code> を使って評価してみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bufio"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bytes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"io"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Read content (text data) from buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">ContentText</span><span class="p">(</span><span class="nx">inStream</span> <span class="nx">io</span><span class="p">.</span><span class="nx">Reader</span><span class="p">)</span> <span class="p">([]</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">scanner</span> <span class="o">:=</span> <span class="nx">bufio</span><span class="p">.</span><span class="nf">NewScanner</span><span class="p">(</span><span class="nx">inStream</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">string</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">scanner</span><span class="p">.</span><span class="nf">Scan</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">list</span><span class="p">,</span> <span class="nx">scanner</span><span class="p">.</span><span class="nf">Text</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">scanner</span><span class="p">.</span><span class="nf">Err</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">list</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Write content (text data) to buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">WriteBuffer1</span><span class="p">(</span><span class="nx">lines</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//write to byte buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">content</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">recode</span> <span class="o">:=</span> <span class="s">"\r\n"</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">line</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lines</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">content</span><span class="p">,</span> <span class="nx">line</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">content</span><span class="p">,</span> <span class="nx">recode</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">content</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Write content (text data) to buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">WriteBuffer1Cap128</span><span class="p">(</span><span class="nx">lines</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//write to byte buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">content</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">128</span><span class="p">)</span> <span class="c1">//128 bytes capacity
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">recode</span> <span class="o">:=</span> <span class="s">"\r\n"</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">line</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lines</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">content</span><span class="p">,</span> <span class="nx">line</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">content</span><span class="p">,</span> <span class="nx">recode</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">content</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Write content (text data) to buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">WriteBuffer1Cap1K</span><span class="p">(</span><span class="nx">lines</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//write to byte buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">content</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1024</span><span class="p">)</span> <span class="c1">//1K bytes capacity
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">recode</span> <span class="o">:=</span> <span class="s">"\r\n"</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">line</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lines</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">content</span><span class="p">,</span> <span class="nx">line</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">content</span><span class="p">,</span> <span class="nx">recode</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">content</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Write content (text data) to buffer (buffered I/O)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">WriteBuffer2</span><span class="p">(</span><span class="nx">lines</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//write to byte buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">content</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewBuffer</span><span class="p">(</span><span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">recode</span> <span class="o">:=</span> <span class="s">"\r\n"</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">line</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lines</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">recode</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">content</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Write content (text data) to buffer (buffered I/O)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">WriteBuffer2Cap128</span><span class="p">(</span><span class="nx">lines</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//write to byte buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">content</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewBuffer</span><span class="p">(</span><span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">128</span><span class="p">))</span> <span class="c1">//128 bytes capacity
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">recode</span> <span class="o">:=</span> <span class="s">"\r\n"</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">line</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lines</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">recode</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">content</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Write content (text data) to buffer (buffered I/O)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">WriteBuffer2Cap1K</span><span class="p">(</span><span class="nx">lines</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//write to byte buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">content</span> <span class="o">:=</span> <span class="nx">bytes</span><span class="p">.</span><span class="nf">NewBuffer</span><span class="p">(</span><span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1024</span><span class="p">))</span> <span class="c1">//1K bytes capacity
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">recode</span> <span class="o">:=</span> <span class="s">"\r\n"</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">line</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">lines</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">recode</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">content</span><span class="p">.</span><span class="nf">Bytes</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>テストコード <code>join_test.go</code> はこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">readFile</span><span class="p">()</span> <span class="p">[]</span><span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">file</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="s">"CollisionsForHashFunctions.txt"</span><span class="p">)</span> <span class="c1">//maybe file path
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">file</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">ContentText</span><span class="p">(</span><span class="nx">file</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">list</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkWriteBuffer1</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="o">:=</span> <span class="nf">readFile</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="o">:=</span> <span class="nf">WriteBuffer1</span><span class="p">(</span><span class="nx">list</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkWriteBuffer1Cap128</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="o">:=</span> <span class="nf">readFile</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="o">:=</span> <span class="nf">WriteBuffer1Cap128</span><span class="p">(</span><span class="nx">list</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkWriteBuffer1Cap1K</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="o">:=</span> <span class="nf">readFile</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="o">:=</span> <span class="nf">WriteBuffer1Cap1K</span><span class="p">(</span><span class="nx">list</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkWriteBuffer2</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="o">:=</span> <span class="nf">readFile</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="o">:=</span> <span class="nf">WriteBuffer2</span><span class="p">(</span><span class="nx">list</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkWriteBuffer2Cap128</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="o">:=</span> <span class="nf">readFile</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="o">:=</span> <span class="nf">WriteBuffer2Cap128</span><span class="p">(</span><span class="nx">list</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">BenchmarkWriteBuffer2Cap1K</span><span class="p">(</span><span class="nx">b</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">B</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="o">:=</span> <span class="nf">readFile</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">.</span><span class="nf">ResetTimer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">b</span><span class="p">.</span><span class="nx">N</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">content</span> <span class="o">:=</span> <span class="nf">WriteBuffer2Cap1K</span><span class="p">(</span><span class="nx">list</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">_</span> <span class="p">=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>のテストについては<a href="https://text.baldanders.info/golang/testing/">以前紹介した</a>が,同じ要領で <code>Benchmark</code> から始まる名前の関数を作るとベンチマーク用のコードとして認識される。
引数には <code>b *testing.B</code> を指定する。</p>
<p>ベンチマークの内訳は以下のとおり。</p>
<table>
<thead>
<tr>
<th style="text-align:left">ベンチマーク名</th>
<th style="text-align:left">処理内容</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><code>BenchmarkWriteBuffer1</code></td>
<td style="text-align:left"><code>[]byte</code> に <code>append</code> する</td>
</tr>
<tr>
<td style="text-align:left"><code>BenchmarkWriteBuffer1Cap128</code></td>
<td style="text-align:left"><code>[]byte</code> に <code>append</code> する( capacity 128B)</td>
</tr>
<tr>
<td style="text-align:left"><code>BenchmarkWriteBuffer1Cap1K</code></td>
<td style="text-align:left"><code>[]byte</code> に <code>append</code> する( capacity 1KB)</td>
</tr>
<tr>
<td style="text-align:left"><code>BenchmarkWriteBuffer2</code></td>
<td style="text-align:left"><a href="http://golang.org/pkg/bytes/"><code>bytes</code></a><code>.Buffer</code> に追記する</td>
</tr>
<tr>
<td style="text-align:left"><code>BenchmarkWriteBuffer2Cap128</code></td>
<td style="text-align:left"><a href="http://golang.org/pkg/bytes/"><code>bytes</code></a><code>.Buffer</code> に追記する( capacity 128B)</td>
</tr>
<tr>
<td style="text-align:left"><code>BenchmarkWriteBuffer2Cap1K</code></td>
<td style="text-align:left"><a href="http://golang.org/pkg/bytes/"><code>bytes</code></a><code>.Buffer</code> に追記する( capacity 1KB)</td>
</tr>
</tbody>
</table>
<p>入力テキストだが,小さいファイルではテストにならない気がしたので,大昔に書いたテキスト <a href="https://baldanders.info/spiegel/archive/CollisionsForHashFunctions.txt"><code>CollisionsForHashFunctions.txt</code></a> を使うことにした。
サイズは70行,7KB ほど。</p>
<h2>テスト結果</h2>
<p>結果は以下のとおり。</p>
<pre tabindex="0"><code>C:>go test -bench WriteBuffer -benchmem
testing: warning: no tests to run
PASS
BenchmarkWriteBuffer1-8 100000 17831 ns/op 37056 B/op 12 allocs/op
BenchmarkWriteBuffer1Cap128-8 100000 20321 ns/op 36992 B/op 11 allocs/op
BenchmarkWriteBuffer1Cap1K-8 100000 19301 ns/op 36096 B/op 8 allocs/op
BenchmarkWriteBuffer2-8 100000 17300 ns/op 33760 B/op 10 allocs/op
BenchmarkWriteBuffer2Cap128-8 100000 19451 ns/op 34992 B/op 9 allocs/op
BenchmarkWriteBuffer2Cap1K-8 100000 15490 ns/op 25712 B/op 6 allocs/op
ok join 12.659s
</code></pre><p>ありゃりゃ。 <a href="http://golang.org/pkg/bytes/"><code>bytes</code></a><code>.Buffer</code> を使ったほうが速いみたい(capacity を大きくとれば)。</p>
<p>それなら,入力テキストを切り詰めて10行,0.3KB にしてやってみる。</p>
<pre tabindex="0"><code>C:>go test -bench WriteBuffer -benchmem
testing: warning: no tests to run
PASS
BenchmarkWriteBuffer1-8 2000000 859 ns/op 1312 B/op 5 allocs/op
BenchmarkWriteBuffer1Cap128-8 2000000 707 ns/op 1248 B/op 4 allocs/op
BenchmarkWriteBuffer1Cap1K-8 2000000 796 ns/op 1376 B/op 2 allocs/op
BenchmarkWriteBuffer2-8 1000000 1686 ns/op 1600 B/op 6 allocs/op
BenchmarkWriteBuffer2Cap128-8 1000000 1411 ns/op 1680 B/op 5 allocs/op
BenchmarkWriteBuffer2Cap1K-8 2000000 980 ns/op 1488 B/op 3 allocs/op
ok join 13.589s
</code></pre><p>今度は <code>[]byte</code> の方が速くなった。</p>
<p>まぁでも予想通りかな。
データのサイズが大きくなればバッファ操作のほうが有利になるのは分かりやすいっちゃあ分かりやすい。</p>
<p>注目すべきは <code>BenchmarkWriteBuffer1Cap128</code> と <code>BenchmarkWriteBuffer1Cap1K</code> で, capacity を 1KB 取ったほうが若干遅くなっている。この辺のチューニングをどうするか,というところなのだろう(実はこれ,環境によって微妙に順位が変わるんだよなぁ)。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://jxck.hatenablog.com/entry/20131123/1385189088">Go でベンチマーク - Block Rockin’ Codes</a></li>
<li><a href="http://qiita.com/Mulyu/items/ed585f2777496f29a725">go言語でベンチマーク - Qiita</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->