camelcaseでgoのbenchをやってみた
結論
- forの入れ子無くなると早いよね
- allocationの減少がパフォーマンスに影響が高い
[]bytes
操作はやい
ベンチ結果
- goのtest benchで実行
go test -bench . -benchmem -count=1
- 1と2の間は実行速度に差があるレベル
- 2と3はallocationが減ってる
BenchmarkCamelize1-4 500000 2216 ns/op 1392 B/op 45 allocs/op BenchmarkCamelize2-4 2000000 953 ns/op 320 B/op 14 allocs/op BenchmarkCamelize3-4 2000000 812 ns/op 224 B/op 8 allocs/op
本体
- 1-3まである
package camelcase import "strings" func camelize1(src string) string { splitedSrcs := strings.Split(src, "_") cameliedStrs := make([]string, len(splitedSrcs)) for splitedSrcIndex, splitedSrc := range splitedSrcs { splitedStrsParts := make([]string, len(splitedSrc)) for i, v := range strings.Split(splitedSrc, "") { if i == 0 { splitedStrsParts[i] = strings.ToUpper(v) } else { splitedStrsParts[i] = strings.ToLower(v) } } cameliedStrs[splitedSrcIndex] = strings.Join(splitedStrsParts, "") } return strings.Join(cameliedStrs, "") } func camelize2(src string) string { splitedSrcs := strings.Split(src, "_") cameliedStrs := make([]string, len(splitedSrcs)) for splitedSrcIndex, splitedSrc := range splitedSrcs { splitedSrcRune := []rune(splitedSrc) cameliedStrs[splitedSrcIndex] = strings.ToUpper(string(splitedSrcRune[0])) + strings.ToLower(string(splitedSrcRune[1:])) } return strings.Join(cameliedStrs, "") } func camelize3(src string) string { splitedSrcs := strings.Split(src, "_") cameliedStrs := make([]byte, 0, len(src)) for _, splitedSrc := range splitedSrcs { splitedSrcRune := []rune(splitedSrc) cameliedStrs = append(cameliedStrs, []byte(strings.ToUpper(string(splitedSrcRune[0]))+strings.ToLower(string(splitedSrcRune[1:])))...) } return string(cameliedStrs) }
package camelcase import ( "log" "testing" ) func TestCamelize1(t *testing.T) { if re := camelize1("AAAAA_Bnnnn_AA_CCCCCCCCC_DDDDDD_XXXXXXX"); re != "AaaaaBnnnnAaCccccccccDdddddXxxxxxx" { log.Fatal("unexpected case: " + re) } } func TestCamelize2(t *testing.T) { if re := camelize2("AAAAA_Bnnnn_AA_CCCCCCCCC_DDDDDD_XXXXXXX"); re != "AaaaaBnnnnAaCccccccccDdddddXxxxxxx" { log.Fatal("unexpected case: " + re) } } func TestCamelize3(t *testing.T) { if re := camelize3("AAAAA_Bnnnn_AA_CCCCCCCCC_DDDDDD_XXXXXXX"); re != "AaaaaBnnnnAaCccccccccDdddddXxxxxxx" { log.Fatal("unexpected case: " + re) } } func BenchmarkCamelize1(b *testing.B) { for i := 0; i < b.N; i++ { camelize1("AAAAA_Bnnnn_AA_CCCCCCCCC_DDDDDD_XXXXXXX") } } func BenchmarkCamelize2(b *testing.B) { for i := 0; i < b.N; i++ { camelize2("AAAAA_Bnnnn_AA_CCCCCCCCC_DDDDDD_XXXXXXX") } } func BenchmarkCamelize3(b *testing.B) { for i := 0; i < b.N; i++ { camelize3("AAAAA_Bnnnn_AA_CCCCCCCCC_DDDDDD_XXXXXXX") } }
Golandのショートカットメモ (Mac)
- 定義元への移動
- プロジェクトウインドウの切り替え
- 指定行へ移動
- プロジェクトのテキストの検索
- Rename
- structureの表示・非表示
- if err != nilの補完
- main関数の補完
- Find Useage
- コメントアウト
- 大文字小文字の切り替え
定義元への移動
Cmd + B
- インタフェースか実装へ移動したい場合
Cmd + Alt + B
プロジェクトウインドウの切り替え
Command + option + `
指定行へ移動
Command + L
プロジェクトのテキストの検索
Shift + Command + F
Rename
fn + Shift + F6
structureの表示・非表示
command + 7
if err != nil
の補完
- err
- tab
- enter
main関数の補完
- main
- tab
- enter
Find Useage
- 使用箇所一覧
fn + option + F7
コメントアウト
- 指定範囲をコメントアウトする
option + command + /
大文字小文字の切り替え
- 選択範囲を大文字小文字に切り替える
command + shift + u
Makefileをいじってみる(2)
- https://www.gnu.org/software/make/manual/make.html
- これをみて色々試してみる
暗黙のルール
- https://www.gnu.org/software/make/manual/make.html#make-Deduces
- https://www.gnu.org/software/make/manual/make.html#Implicit-Rules
main.o : main.c defs.h
cc -c main.c
- この記述が暗黙のルールを採用すると
main.o : defs.h
- ここまで短くなる
- .cを推論している
ディレクトリのクリーンアップ
- https://www.gnu.org/software/make/manual/make.html#Special-Targets
- ここにある
.PHONY
を使用するとファイルの存在や更新時刻に関係なくレシピを実行する
clean: rm edit $(objects)
- この場合にエラーが起きる可能性を排除してくれる
.PHONY : clean clean : -rm edit $(objects)
Makefileにふくまれているもの
以下の5つが含まれている
- explicit rules
- implicit rules
- variable definitions
- directives
- comments.
includeと上書き
- Makefileはincludeで他のMakefileを読み込める
- また、一部を上書きすることも出来る
- http://higepon.hatenablog.com/entry/20061127/1164639881
- ここに詳しい
foo: frobnicate > foo %: force @$(MAKE) -f Makefile $@ force: ;
Makefileをいじってみる(1)
- https://www.gnu.org/software/make/manual/make.html
- これをみて色々試してみる
基本的なルール
- ターゲット: 前提条件
処理内容
target … : prerequisites … recipe … …
- 実例を見ると以下の様になる
- この
edit
の場合は、main.oからutils.oまでを条件にしてcc
を実行してeditを作成している
edit : main.o kbd.o command.o display.o insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o
- 名前が被っているので面倒なのだが
前提条件
は以下になる- 要は依存している処理をtargetの横に書いている
- それを元にしてrecipeが実行される
main.o : main.c defs.h cc -c main.c kbd.o : kbd.c defs.h command.h cc -c kbd.c command.o : command.c defs.h command.h cc -c command.c display.o : display.c defs.h buffer.h cc -c display.c insert.o : insert.c defs.h buffer.h cc -c insert.c search.o : search.c defs.h buffer.h cc -c search.c files.o : files.c defs.h buffer.h command.h cc -c files.c utils.o : utils.c defs.h cc -c utils.c
変数定義
- さっき出てきたeditの前提条件を変数にまとめると
- こう定義して
objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
- 以下の様に参照出来る
edit : $(objects) cc -o edit $(objects)
zipのパスワードについてきになったこと
- goのzipでパスワード付きのzipを解凍しようとしたんですが
- https://golang.org/pkg/archive/zip/
- 公式を見ている限りその機能がない
- 結論からいうと
- https://github.com/yeka/zip
- こちらのライブラリを使用すると解凍することが出来る
- 気になったのは、なんでこれは公式のライブラリに無いのかということ
zip.StandardEncryption zip.AES128Encryption zip.AES192Encryption zip.AES256Encryption
- yeka/zipのドキュメントを見るとこれが使えるという話になっている
AES
は標準の仕様では無いのだろうか- https://support.pkware.com/display/PKZIP/APPNOTE
- ここが標準らしい
- 他にも拡張が存在するらしいのでそちらがデファクトになってしまっているのではないだろうか
goのmainの始まる前(1)
- そう言えばgoのmainの始まる前をまじまじと折ったことはないなと
- 思ったのでちょっとだけ追う
runtime.main
から呼ばれているようだ
// The main goroutine. func main() {
getg()
が実行されてcurrentのg
が返される
g := getg()
type g struct { stack stack // offset known to runtime/cgo stackguard0 uintptr // offset known to liblink stackguard1 uintptr // offset known to liblink _panic *_panic // innermost panic - offset known to liblink _defer *_defer // innermost defer m *m // current m; offset known to arm liblink sched gobuf syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc stktopsp uintptr // expected sp at top of stack, to check in traceback param unsafe.Pointer // passed parameter on wakeup atomicstatus uint32 stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus goid int64 schedlink guintptr waitsince int64 // approx time when the g become blocked waitreason waitReason // if status==Gwaiting preempt bool // preemption signal, duplicates stackguard0 = stackpreempt paniconfault bool // panic (instead of crash) on unexpected fault address preemptscan bool // preempted g does scan for gc gcscandone bool // g has scanned stack; protected by _Gscan bit in status gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; TODO: remove? throwsplit bool // must not split stack raceignore int8 // ignore race detection events sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine sysexitticks int64 // cputicks when syscall has returned (for tracing) traceseq uint64 // trace event sequencer tracelastp puintptr // last P emitted an event for this goroutine lockedm muintptr sig uint32 writebuf []byte sigcode0 uintptr sigcode1 uintptr sigpc uintptr gopc uintptr // pc of go statement that created this goroutine ancestors *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors) startpc uintptr // pc of goroutine function racectx uintptr waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order cgoCtxt []uintptr // cgo traceback context labels unsafe.Pointer // profiler labels timer *timer // cached timer for time.Sleep selectDone uint32 // are we participating in a select and did someone win the race? gcAssistBytes int64 }
- max stack sizeを設定
- 64bitだと1Gで32bitだと250Mに設定
if sys.PtrSize == 8 { maxstacksize = 1000000000 } else { maxstacksize = 250000000 }
- メインゴルーチンをmain threadに固定する
lockOSThread()
//go:nosplit func lockOSThread() { getg().m.lockedInt++ dolockOSThread() }
- 現在のgへ
m.lockedg
とlockedm
へセット
func dolockOSThread() { if GOARCH == "wasm" { return // no threads on wasm yet } _g_ := getg() _g_.m.lockedg.set(_g_) _g_.lockedm.set(_g_.m) }
- runtimeの初期化
runtime_init() // must be before defer
- 中身どこだろうなこれ
//go:linkname runtime_init runtime.init func runtime_init()
- gcの初期化
bgsweep
をgoroutineで実行
func gcenable() { c := make(chan int, 1) go bgsweep(c) <-c memstats.enablegc = true // now that runtime is initialized, GC is okay }
- これがsweepの実体
func bgsweep(c chan int) { sweep.g = getg() lock(&sweep.lock) sweep.parked = true c <- 1 goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) for { for sweepone() != ^uintptr(0) { sweep.nbgsweep++ Gosched() } for freeSomeWbufs(true) { Gosched() } lock(&sweep.lock) if !isSweepDone() { // This can happen if a GC runs between // gosweepone returning ^0 above // and the lock being acquired. unlock(&sweep.lock) continue } sweep.parked = true goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) } }
- で、やっとmainを呼んでいる箇所がきた
fn := main_init // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime fn() close(main_init_done)
とりあえず、今日はこのぐらいにする。 気が向いたら続き
fdというファイルを見つけるのに便利なコマンド
- https://github.com/sharkdp/fd
- 最近はファイルを発見するのに
fd
というコマンドを使用している - installも使用方法も楽なのでおすすめ。