V言語でアプリ後日談
連作障害
気の早い農家の方は、田んぼを耕したり、あぜを作ったりと、今年の農業サイクルを開始してる。もうすぐ、田植え用の苗床作成にも着手されるのかな? とか考えながら歩いていたら、ふと疑問が湧いてきた。
連作障害って聞いたことないだろうか? 農家や家庭菜園をやる人の常識。 今さら聞けない連作障害の原理。なぜ連作障害は生じるのか
稲は毎年同じ場所に作るけど、この障害は出ないの? 上記では野菜類って事でくくっているけど、農林水産省の指標では、毎年実をつけて枯れるものは野菜の扱い。稲もそうだよね。
疑問だ、ぎもんだ。 こんな?は、私だけではなかった。
そういう事だったのね。ちっとも知りませんでした。
消えなくて困った
前回一応アプリが完成したけど、後日談と言う事で、補足しておく。
アプリと言っても、基本はREPLだ。READしてEVALしてPRINT。loopは無いけどね。だから、アプリを作る時は、それに沿って作っていく事になる。
CSVファイルから読み込み、必要なデータを計算と言うか選り分け、グラフはgnuplotに任せて印刷。gunplotへのデータ渡しはファイルになるんで、好都合な事に、結果をcatとかで簡単に確認出来る。出来たファイルを消す方法を確認して、gnuplotに手を出した。どうやら、動くようになったので、総合試験って事で、不必要になったファイルを消す関数を有効化した。
が、さっぱりファイルが消えないのである。BUGを呼び込んでしまったかと一瞬焦ったぞ。
よく確認したら、gnuplotを呼び出した所で終了するコードになってた。だから、最後の方のos.rmには絶対に届かない。
mainの一部
: exec(['-c', 'gnuplot -e ${ag('am.dat')} topdf.plt >/dev/null 2>&1']) os.rm('topdf.plt') ? :
上記で、>/dev/null 2>&1 してるのは、gnuplotが出す(統計データ集計)データを、闇に葬るため。鬱陶しい表示は要らない。
そこで、昔にやった、サブプロセスですよ。
// exec shell (this run on unix, windows can not run) fn exec(args []string) { pid := os.fork() if pid > 0 { os.wait() return } else { os.execve('/bin/sh', args, []) or { panic(err) } // NO REACH HERE } }
最初はox.execveしか書いていなかったんで、nbldvが/bin/shで上書きされてしまってた(いわゆる置き換えだ)。そこで、上記のように、親子関係をforkで作って、子に/bin/shを任せる。親は、子が終了するまでwaitで待つって事にした。いにしえのunix時代からある作法である。
fork,wait
この大事なforkとwaitだが、残念ながら下記の状態になってる。
vlib/os/os_c.v
// fork will fork the current system process and return the pid of the fork. pub fn fork() int { mut pid := -1 $if !windows { pid = C.fork() } $if windows { panic('os.fork not supported in windows') // TODO } return pid }
waitもwindowsは未サポート状態である。TODOに期待しよう。それとも、ファイルを消す事は諦めて、gnuplotだけでも動くようにする? execveをwindowsで動かすには、どうすればいいのかな? 誰か実験乞う。
how do golang
その点、golangでは、
exec.Command("gnuplot", "-e", ag("am.dat"), "topdf.plt").Run()
これだけで、linuxでもWindowsでも動いてしまうからなあ。どんな仕組みになってるの?
exec…Run() を手がかりに追って行くと、あちことを引き回された挙句、 src/syscall/exec_windows.go
あたりに導かれた。unix用は、 exec_unix.go
になるかな。まずは慣れてる?unix用を見る。
// StartProcess wraps ForkExec for package os. func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle\ uintptr, err error) { pid, err = forkExec(argv0, argv, attr) return pid, 0, err }
対するWindowsの方は? 見よう見まねなんだけど、unixのfork-execとは違う方式なの?
func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) { // Windows CreateProcess takes the command line as a single string: // use attr.CmdLine if set, else build the command line by escaping // and joining each argument with spaces err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, si, pi)
余り深入りするなって警告灯が灯っていますよ。けど、いきがかり上、windows subprocees なんてのでggると、pythonばかりヒットする。
ならば、windows fork exec ぐらいに検索語を変えてggれ。
Microsoftのロゴが出てるんで、公式な方法なんでしょうな。それにしてもオイラーは機転が効かないな。なんの為にgolangのソースを見てるの。検索語の答えがずばりCreateProcessって出てるじゃん。unixに毒されているな。
そうそう、毒されていると自認してるなら、dupあたりがV語に有るか調べて桶。
sakae@pen:~/src/v/vlib$ find . -name '*.v' | xargs grep 'pub fn' | grep dup ./builtin/linux_bare/old/linuxsys_bare.v:pub fn sys_dup2(oldfd int, newfd int) (i64, Errno) { :
pipeも同じ所に有った。けどリナ系だけの特典なの? ちょっと気になるな。
隠れた名機
上記のpipeを探している時 process_nix.c.v
なんて所に素敵な物が隠れている事を見つけた。こんな良いもの何故pubにしない? 深い理由が有りそうだな。
fn (mut p Process) unix_spawn_process() int { mut pipeset := [6]int{} if p.use_stdio_ctl { C.pipe(&pipeset[0]) // pipe read end 0 <- 1 pipe write end C.pipe(&pipeset[2]) // pipe read end 2 <- 3 pipe write end C.pipe(&pipeset[4]) // pipe read end 4 <- 5 pipe write end } pid := fork() : execve(p.filename, p.args, p.env) or { eprintln(err) exit(1) } return 0 }
process_windows.c.v
なんてのにも、そこそこコードが載ってるな。golangの全方位外交と違って、ソースがすっきりしてるのが良い。
profile
簡単に出来たんでやってみたな。どんな風にやってるか、眺めてみる。対象は出口が複数有る main__exec
ob$ v -o p.c -profile zz.txt nbldv.v
まずは、プロファイル用のC語ファイルを作成。そして拾い出し。
double vpc_main__exec = 0.0; u64 vpc_main__exec_calls = 0; : if (vpc_main__exec_calls) fprintf(fp, "%14llu %14.3fms %14.0fns %s \n", vpc_main__exec_calls, vpc_main__exec/1000000.0, vpc_main__exec/vpc_main__exec_calls, "main__exec" ); : VV_LOCAL_SYMBOL void main__exec(Array_string args) { double _PROF_FN_START = time__vpc_now(); vpc_main__exec_calls++; // main.exec int pid = os__fork(); if (pid > 0) { os__wait(); // defer_profile_code vpc_main__exec += time__vpc_now() - _PROF_FN_START; return; : // defer_profile_code vpc_main__exec += time__vpc_now() - _PROF_FN_START; }
呼び出し回数のカウンターと、累積時間の保持用変数を用意。if文で、呼び出された関数だけを、印字。あれ? fprintfが堂々と使われているぞ。以前探した時は見つからなかったんだけどな。
関数に入った所でタイマースタート。そして、呼び出し回数カウンターを増加。それぞれの出口の所で、実行時間を加算してる。これで、くまなくデータ収集出来るって訳か。統計的なんて言う、けちな収集はしてないんだな。
free
profileを取ってみると、freeもしてるな。mallocの回数と比べて、余りに非対称さが気にはなるけど。礼儀正しいfreeをしてるのは、何処の人?
1208 1.574ms 1303ns v_malloc 144 0.452ms 3139ns realloc_data 514 0.603ms 1173ns vcalloc 5 0.093ms 18676ns v_free
(gdb) bt #0 v_free (ptr=0x2af27da00) at /tmp/v/nbldv.3693977796883229390.tmp.c:6635 #1 0x0000000000444d65 in os__getwd () at /tmp/v/nbldv.3693977796883229390.tmp.c:11930 #2 0x00000000004700ab in _vinit (___argv=0x7f7fffff79f8, ___argc=1) at /tmp/v/nbldv.3693977796883229390.tmp.c:14272 #3 0x0000000000470116 in main (___argv=0x7f7fffff79f8, ___argc=1) at /tmp/v/nbldv.3693977796883229390.tmp.c:14281
危険なデータなんで、cloneしてコピーした上で、元のエリアをfreeしてた。
(gdb) bt #0 v_free (ptr=0x0) at /tmp/v/nbldv.3693977796883229390.tmp.c:6635 #1 0x0000000000421bfe in array_free (a=0x7f7fffff6f28) at /tmp/v/nbldv.3693977796883229390.tmp.c:6043 #2 0x000000000044dc0d in flag__FlagParser_parse_value (shorthand=105 'i', longand=<error reading variable: value of type `string' requires 4294967295 bytes, which is more than max-value-size>, fs=0x299552580) at /tmp/v/nbldv.3693977796883229390.tmp.c:13265 :
自前で不要になるエリアを見繕っておき、用が済んだら解放してた。超局所的なGCだな。それぞれの作者さんの癖が出てるみたいで面白い。
gauche
慣れ親しんだgaucheにもプロファイルが仕込んであった。以下は取扱説明書より抜粋。
時間プロファイラは統計的標本化をおこなっていることに注意してください。 プロファイラは10ミリ秒ごとにプロセスに割込んで、その時点で実行されている関数を記録 します。ナノ秒オーダの関数呼出しごとの個別の実行時間に比べる と、このサンプリング レートはかなり粗いものです。しかしながら、プロ グラムの実行時間が長ければ、各関数 ごとの標本分布は関数ごとの消費時間を ほぼ反映しているだろうと期待できます。 プロファイラはそれ自身にオーバヘッドがあります。通常は、処理時間が 20-30% 増加します。
V語は全て計測するから時間がかかる。対して、サンプリング方式は、文字通りサンプリングなので、オーバーヘッドは小さい。プロファイルの目的は、時間がかかっている所を見つける事だから、細かい事は気にする必要無いな。
多分V語方式の方が、実装が楽だったのでしょう。V語さんも余裕が出来たら、統計的サンプリング方式を実現して欲しいなあ。
golang
当然golangにも有ると思われるのでggしたよ。
10msでの統計的プロファイリングでした。こう書くとFBIみたいだな。
折角なので、例に倣ってプロファイルとやらをやってみる。
sakae@pen:~/go/src/bld$ go get github.com/pkg/profile go: downloading github.com/pkg/profile v1.5.0 sakae@pen:~/go/src/bld$ sudo apt install graphviz
外部パッケージだから、お取り寄せ。綺麗な図を書くためにアプリ導入。
import ( "github.com/pkg/profile" : func main() { defer profile.Start(profile.ProfilePath(".")).Stop() :
ターゲットのbld.goに埋め込み。
sakae@pen:~/go/src/bld$ go build bld.go bld.go:14:10: no required module provides package github.com/pkg/profile: go.mod file not found in current directory or any parent directory; see 'go help modules' sakae@pen:~/go/src/bld$ touch go.mod sakae@pen:~/go/src/bld$ go build bld.go go: no module declaration in go.mod. To specify the module path: go mod edit -module=example.com/mod sakae@pen:~/go/src/bld$ go mod edit -module=mod sakae@pen:~/go/src/bld$ go build bld.go bld.go:14:10: no required module provides package github.com/pkg/profile; to add it: go get github.com/pkg/profile sakae@pen:~/go/src/bld$ go get github.com/pkg/profile go get: added github.com/pkg/profile v1.5.0 sakae@pen:~/go/src/bld$ go build bld.go
話が違うじゃんgo.modとかが必要らしい。そんなのどうやって作るの? ここは得意な戦法を繰り出す事にする。そうさ、ダミーのgo.modを作っちゃえ。多分文句を言ってくるだろうけど、goの事だから、あーせいとかのhelpが出てくるに違いない。
知らないコマンドを叩け。何とか会社はおこがましいので、訳も分からず適当に入力。少し先に進んで、もう一度プロファイルをgetさせられた。そのカイが有って無事にコンパイル出来たみたい。
sakae@pen:~/go/src/bld$ cat go.mod module mod go 1.16 require github.com/pkg/profile v1.5.0 // indirect sakae@pen:~/go/src/bld$ cat go.sum github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug= github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
何か、管理ファイルが出来ていたけど、気にしない。それじゃ、プロファイル用にcsvファイルを水増しする。
sakae@pen:~/go/src/bld$ cp current.csv a sakae@pen:~/go/src/bld$ cat a a a a a a a a a a a a >b sakae@pen:~/go/src/bld$ cat b b b b b b b b b b >c sakae@pen:~/go/src/bld$ cat c c c c c c c c > current.csv sakae@pen:~/go/src/bld$ wc a b c current.csv 62 62 1178 a 744 744 14136 b 7440 7440 141360 c 59520 59520 1130880 current.csv
3月のデータを12個連結してほぼ1年分、それを10回連結して10年分、それを8回連結して80年分のデータにした。これを餌にしてプロファイルする。
sakae@pen:~/go/src/bld$ ./bld 2021/04/23 13:47:53 profile: cpu profiling enabled, cpu.pprof 2021/04/23 13:47:53 profile: cpu profiling disabled, cpu.pprof sakae@pen:~/go/src/bld$ go tool pprof -http=":8081" bld cpu.pprof Serving web UI on http://localhost:8081
図は出てきたけど、わけわかめだな。それに下の方が欠けてしまっているし。
frame graphってのが見易いな。mainの前後にruntimeの処理が入っている。それに結構な時間がかかっていて、mainが走っているのは、全体の60%だなんて事が分かる。
やっぱりオイラーは図よりも文字列が好きだぞ。
v-mode
emacsにv-modeが来てたので、入れてみたんだけど、ファイルをセーブすると、v fmt して、常に模範的コードにフォーマットしてくれる。それは嬉しい事なんだけど、続いてctagsしちゃう。これ、鬱陶しいぞ。
/bin/bash: -c: line 0: `ctags --languages=-v --langdef=v --langmap=v:.v --regex\-v=/[ \t]*fn[ \t]+(.*)[ \t]+(.*)/\2/f,function/ ... -e -R . /home/sakae/src/v/vlib'
何度言ったら分かる! /bin/bashの決め打ち止めれ。仏の顔も3度までだぞ。と、ぶつくさ言いながら、v-mode.elを少々改造した。
(defun v-after-save-hook () "After save hook." (when (eq major-mode 'v-mode) (v-format-buffer) ;; (if (not (executable-find "ctags")) ;; (message "Could not locate executable '%s'" "ctags") ;; (v-build-tags)) ))
これで、ファイルをsave時のメッセージがすっきりしたぞ。
Reformatted file: /tmp/nbldv/nbldv.v
learn_v_in_y_minutes
v-modeのメニューに、有用なリンクが載ってた。前回の最後に紹介しておいた、素敵なVと言うやつである。 その中に learnvinyminutes なんてのが有った。得意のやつである。昔流行ったね。有志がコメントを日本語化されていた。もう少し、早く気が付いていたら、前回の移植ももっとスマートに行えたはず。ちょっと失敗したな。これから手を出そうと言う人は、見ておくと吉。
Forums
そして、比較的自由に書き込めそうなフォーラムも挙げてあった。
これ、Vがgoよりも少ないコードで書けると言う、自慢。あっちの水は甘いぞというやつである。見てると楽しいぞ。
etc
ドコモ「わたしムーヴ」アプリのサービス終了により、オムロンの血圧計などが利用不能に
オイラーの血圧計はオムロン製。但し年寄り用で、測定値が表示されるだけって言うローテクな奴。ゆえに、測定値をチラ裏にメモして、10日に一度ぐらい今までやってきたアプリに入力。 PDFになるんで、USBに入れて、コンビニで印刷、医者の所へ行って、ヤクを貰って来るって運用だ。
医者から貰った血圧手帳は自分でグラフを書くだけって奴。平均も偏差値も勿論出てこない。意味ないじゃん。経年劣化なんて知る術も無い。ってな事で、アプリを自作したのさ。それから、昔に買ったプリンターはインク供給が停止されちゃって、ただの箱になってしまったしね。
他人に頼らないで自助努力ですよ。何もかもネットに上げて、そこで管理って、便利なようで不便だな。運用者にデータが筒抜けになし、運用者次第だからね。
スマホのアプリとか見てると、みんなグーグルに持って行かれるってのがよく分かる。マップで、どこを歩いていたかサツより良く知ってる。スケジュール表で、何時どんな行動を起こすか、バレバレ。無料で利用出来るって甘い言葉に騙されるな。慈善事業やってるわけじゃないからね。
奴らは、データ奴隷を多数抱えて、AIを研ぎ澄まし、世界征服を狙ってるのさ。
ネットに距離を置きましょう。まあ、人それぞれだから、どうでもいいけど。
さくらのレンタルサーバ/マネージドサーバ】サーバコントロールパネルへのログインに「2段階認証」を導入
それはいいんだけど、ここにもグーグルが顔を出しているなあ。恐いこっちゃ。