その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

設計Night2018で話してきました

connpass.com

スライドは以下です.

id:nkgt_chkonkさんに「パフォーマンス視点でのソフトウェアの設計というテーマでなんか話せない?」と持ちかけられ,当時色々考えていたこともあり「やってみるか!」という感じの経緯でできたのがこのトークです.


言いたかったことはスライド終盤に書いたように

  • 一発で良い設計にたどり着ければ最高だけどなかなかそうもいかんよね〜
  • 良い設計とは「変化に耐えうること」「変更にあたってコストが低いこと」ではないか
  • 設計には段階がある
  • 第一段階としての良い設計は「最適化の余地がある設計」ではないか

ということです.「早すぎる最適化」はもちろん良くないことですが,しかし「最適化の余地を残しておくことは重要」というような認識です.


スライド中に出てくる「美しいものは空気抵抗が少ない」というのは新幹線を開発した三木忠直さんという方の言葉で,つまり「美しいものは速い」という意味なんですが *1 ,かねがね「いい言葉だな〜」と思っていたので引用させてもらいました.困ったときはそっちの方に倒す,という感じで日々を暮らしています.


なお,この発表はズルをしていて,キュー(バッファ)に使うミドルウェアのコストに全く言及していませんでした.しかし前時代と比較してキューのようなミドルウェアはオンデマンドで利用しやすくなった,そして運用コストが下がって使いやすくなったというのはあると思います.Amazon KinesisやSQSやGoogle Cloud Pub/Subなどといったクラウドサービスがキュー(あるいはpub/sub)サービスを提供しているのが大きいと思います.以前は自分でマシンを確保して「ActiveMQを入れるぞ〜〜〜」みたいな感じだったわけですから……


ところで発表の冒頭でも言ったのですが,僕は割と野生の勘でソフトウェア設計をしがち (もちろん理論は基礎として持ちつつ) で,こういう感じで体系的に言語化するのが得意な方ではないのですが、一丁頑張ってやってみましたところ自分の中でも理解が整理されたり深まったりしたので良かったですねという感じです.


設計Night2018,いいイベントでした.ありがとうございました.

*1:必ずしも一般化できる話ではないことは承知していますが

stringsを使ってどのgoのバージョンでコンパイルしたバイナリかをサクッと取得する

How to find out which Go version built your binary | Dave Cheney

stringsを使わないちゃんとしたやり方はこちら.


雑に取るならstringsコマンドを使うとそれっぽいのが取れます.

$ strings main | grep go1[.]
stack=[cgocheckdebugcpugo1.11.1runnableruntime.scavengeunknown( (forced) -> node= blocked= defersc= in use)

なるほど,これはgo1.11.1でコンパイルされたバイナリ.非常にスッキリしています.

$ strings main | grep go1[.]
, not 390625<-chanArabicAugustBrahmiCarianChakmaCommonCopticFridayGOROOTGothicHangulHatranHebrewHyphenKaithiKhojkiLepchaLycianLydianMondayRejangSCHED SundaySyriacTai_LeTangutTeluguThaana[]bytechan<-efenceerrno go1.11objectpopcntselectstringstructsweep uint16uint32uint64 (scan  (scan) MB in  Value> dying= locks= m->g0= nmsys= s=nil
/tmp/test/bin/go1.11
...

これは1.11でコンパイルされている様子.1.11.1と比べてごちゃごちゃ度が増していて,かつパス情報が下にずらずらと列挙されていますね.

$ strings main | grep go1[.]
stack=[cgocheckgo1.10.4runnableruntime.scavengeunknown( (forced) -> node= blocked= defersc= in use)
/tmp/test/bin/go1.10.4
...
Go cmd/compile go1.10.4

1.10.4です.1.11と比較してスッキリとした風味.パス情報が列挙されており,かつその下にはGo cmd/compile go1.10.4という非常にわかりやすい文字列が入っています.親切!

$ strings main | grep go1[.]
value=cs     float32float64fs     gctracego1.9.7gs     invalidpanic: r10    r11    r12    r13    r14    r15    r8     r9     rax    rbp    rbx    rcx    rdi    rdx    rflags rip    rsi    rsp    runningsyscalluintptrunknownwaiting goal
/Users/moznion/tmp/test/bin/go1.9.7/src/fmt/scan.go
...
Go cmd/compile go1.9.7

1.9.7も1.10.4と似ていますね.Go cmd/compileが付いていて良い時代.

$ strings main | grep go1[.]
value=cs     float32float64fs     gctracego1.8.7gs     invalidpanic: r10    r11    r12    r13    r14    r15    r8     r9     rax    rbp    rbx    rcx    rdi    rdx    reflectrflags rip    rsi    rsp    runningruntimesyscalluintptrunknownwaiting goal
/Users/moznion/tmp/test/bin/go1.8.7/src/fmt/doc.go
...

1.8.7のようです.Go cmd/compileが無いですね.つまりこのマーカーは1.9から1.10.xまでの短い命だったのですね……


というわけでサクッと取れて便利でした.

Released lua-benchmarker

I've released lua-benchmarker that is a benchmarker for lua code. This benchmarker is for taking microbenchmark.

github.com

This library is available on luarocks. You can install this by luarocks install benchmarker.

luarocks.org

For example, write a benchmark code like following:

local benchmarker = require 'benchmarker'

benchmarker.new({
    ["insert append"] = function ()
        local arr = {}
        for i=1,1000 do
            table.insert(arr, i)
        end
    end,
    ["index append"] = function ()
        local arr = {}
        for i=1,1000 do
            arr[i] = i
        end
    end,
}):warmup(10):run_within_time(1):timethese():cmpthese()

then execute this code, the result is the following:

starting warming up: 10

Score:

index append:  1 wallclock secs ( 1.05 usr +  0.00 sys =  1.05 CPU) @ 51124.51/s (n=53695)
insert append:  1 wallclock secs ( 1.06 usr +  0.00 sys =  1.06 CPU) @ 9185.70/s (n=9760)

Comparison chart:

                    Rate  index append  insert append
   index append  51125/s            --           457%
  insert append   9186/s          -82%             --

Yes, this output format is taken from Benchmark.pm. This benchmarker is inspired by Benchmark.pm and tokuhirom/nanobench.

NOTE: This utility supports multi-platform (i.e. Windows is also supported!!).

Enjoy.

Elasticsearchに実際に書き込むテストなんかで「indexへの反映に時間がかかって困る〜〜」って時

Elasticsearchに実際に書き込む系のテストをするとき,例えば「fixtureを作ってindexしてクエリ打ってちゃんと結果が取れるかどうか」みたいなことをやると思うんですが,fixtureをindexする際にそのデータがindexに反映されるまでにrefresh_intervalぶんの時間がかかってつらい!!! というときにどうするかという話です.

Plan A. refresh_interval をめっちゃ短くする

要はrefresh_intervalがめっちゃ短ければ解決する(こともある)話なのでそれをやるという方法.上記のtweetで「1000ms」とか言ってるのはあくまでデフォルト値なので,それをカスタムしてやると良いという話です.

{
  "settings": {
    ...
    "refresh_interval": "1ms"
  }
}

などとしてやればindexへのdataの反映周期が1msecになるのでまあ充分でしょう,という割り切った感じです.テスト用途であればほぼ多くの場合はこれでも動作すると思います.コード自体に手を入れる必要がないのは良さそう.
が,間違えてもproduction環境に入れてはならない設定なのでindex templateを別個に管理する必要が出てくるでしょう.

Plan B. index refresh APIを叩く

www.elastic.co

fixtureを投入してから明示的にindex refresh APIを叩くという方法.叩くとデータが即座に近くindexに反映されるようになるので用途としてはマッチすると思います.これであればproductionのindex templateをそのまま流用できるので便利ですが,まいどまいどrefresh APIを叩くコードを挟まなければならないので,そこは何らかのtest utilityみたいなものでカバーするなどといった工夫が必要そうです.


Plan C. ?refreshをクエリに付ける

www.elastic.co

更新系のクエリを発行する際に?refreshというクエリパラメータをつけると,こちらも即座に近くindexにデータが反映されるようです.お手軽!




他にもあるかもですが,用途と場合に応じたものを使うと良いと思います.

テストが速くなってよかった!

Released lua-cputime

I had released lua-cputime.

github.com

This package also had been shipped to luarocks.

luarocks.org

This library provides functions to measure the CPU time (i.e. user time and system time). The lua runtime doesn't support such functions, so this library bridges to native C's sys/time.h and sys/resource.h and expose getrusage() function as get_process_cputime(), get_children_process_cputime() and get_thread_cputime() *1.

As you can see, this library doesn't support the Windows environment because it associate to sys/time.h and sys/resource.h. I have a will to support the Windows, but I'm not familiar with that. I'm happy if a patch is sent.




[following description is outdated; the implementation has been changed]

This library provides a function to measure the CPU time (i.e. utime, stime, cutime and cstime). The lua runtime doesn't support such function, so this library bridges to native C's sys/times.h and expose times() function as get_cputime().
As you can see, this library doesn't support the Windows environment because it bridges sys/times.h. I have a will to support the Windows, but I'm not familiar with that. I'm happy if a patch is sent.

*1:`get_thread_cputime()` is only supported by Linux

Released lua-url-encode

I had released lua-url-encode.

github.com

And I also had shipped that to luarocks. You can try to install this with luarocks install urlencode.

luarocks.org

This package is a URL encoder/decoder library for lua that is written in C (with lua's native extension mechanism).The library aims (and realized) to be a faster implementation of URL encoding and decoding:

  • encoding is about 64 times faster than pure lua implementation
  • decoding is about 39 times faster than pure lua implementation


Note: This library is a ported implementation from p5-url-encode-xs.