Rの中
R 4.0.3 Coming
–verbose
R –help してみたんだ。そしたら面白いオプション発見。よりおしゃべりになってねって奴だ。で、何を喋り出すかと思ったら、下記のようだった。
sakae@pen:~$ R --verbose 'verbose' and 'quietly' are both true; being verbose then .. now dyn.load("/usr/lib/R/library/methods/libs/methods.so") ... R version 3.5.2 (2018-12-20) -- "Eggshell Igloo" : Garbage collection 1 = 0+0+1 (level 2) ... 11.9 Mbytes of cons cells used (35%) 3.1 Mbytes of vectors used (5%) : now dyn.load("/usr/lib/R/site-library/dplyr/libs/dplyr.so") ... Garbage collection 7 = 6+0+1 (level 0) ... 22.3 Mbytes of cons cells used (66%) 5.4 Mbytes of vectors used (8%) ending setup_Rmainloop(): R_Interactive = 1 {main.c} >R_ReplConsole(): before "for(;;)" {main.c} >
メモリーどれぐらい使ってんねんって報告。もろにscheme丸出しだ。違うのは、ユーザーが使うvectorが、独立してる事。schemeだと、コードエリアもデータエリアも糞みそ一緒で、consエリアを共有するんだけど、Rは一応分離してんのね。
Rの立ち上がりをgdbで観測したい。R -d gdb が最も素直な方法だけど、それじゃソースを見ながらと言う訳にはいかない。ここはもうemacsからって事で、下記を書いた。
debian:main$ cat go.sh #! /bin/sh export R_HOME=/home/sakae/MINE/lib/R export LD_LIBRARY_PATH=/home/sakae/MINE/lib/R/lib export GO=/home/sakae/MINE/lib/R/bin/exec/R emacs -f gdb #gdb $GO
これを起動してemacsのminibufferで $GO って叩くだけ。(流行りの go to eat じゃ無いよ)
が、ソースエリアが指定されない。それならばと言う事で
vbox$ cat .gdbinit dir /home/sakae/src/R/src/main:/home/sakae/src/R/src/unix
こんなのを用意。が、これも読んでくれない。
(gdb) source ./.gdbinit ./.gdbinit: No such file or directory. (gdb) ! pwd /home/sakae/MINE/lib/R/bin/exec
なにか、変な所に連れ込まれているな。ちょいと面倒だけど、cdしたよ。見るべき所は、 setup_Rmainloop
あたりね。
後で知ったんだけど、 Debugging R from within Emacs こんなのがFAQに出てた – FAQを自分で発見するって、車輪の再発明、かな?
コマンドオプションとは、ちと違うけど、同類項を発見。 Environment variable index じっくり見ておくと、幸あれ、かな?
ちょいとgcがらみ。どれぐらい時間がかかった?
> gc.time() [1] 0.136 0.012 0.149 0.000 0.000
左からユーザー時間、システム時間、トータル時間、残りは子プロセスのそれ。
(gdb) bt 6 #0 RunGenCollect (size_needed=0) at memory.c:3125 #1 R_gc_internal (size_needed=size_needed@entry=0) at memory.c:3125 #2 0x000055edeb1812e5 in R_gc () at memory.c:2983 #3 0x000055edeb18190d in do_gc (call=<optimized out>, op=<optimized out>, args=0x55edeedc1e58, rho=<optimized out>) at memory.c:2075
gc()すると、こんな感じになる。
BEGIN_SUSPEND_INTERRUPTS { R_in_gc = TRUE; gc_start_timing(); B gens_collected = RunGenCollect(size_needed); gc_end_timing(); R_in_gc = FALSE; } END_SUSPEND_INTERRUPTS;
gc中は、割り込み禁止モードで走ってる。これって、スレッドも禁止って事なのかな。
–gui=Tk
なんてオプションを知った。これを付けて起動すると、Tkで書かれたコンソールが出て来て懐かしい趣だ。嬉しいのは、help(hoge)とか、 demo() なんてやると、リストが別画面になって表示される事。
ただ、たまに落ちる事がある。
> help(gc) *** caught segfault *** address 0x13911f3c000, cause 'memory not mapped' Possible actions: 1: abort (with core dump, if enabled) 2: normal R exit 3: exit R without saving workspace 4: exit R saving workspace Selection: 3 ob$ R --gui=Tk Segmentation fault (core dumped)
落ちるのも、これまた楽し。
ob$ gdb /usr/local/lib/R/bin/exec/R R.core GNU gdb (GDB) 7.12.1 : Reading symbols from /usr/local/lib/R/bin/exec/R...(no debugging symbols found)...done. [New process 367218] Core was generated by `R'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x0000013933724b03 in Tcl_UtfToUniChar () from /usr/local/lib/libtcl85.so.1.8 (gdb) bt #0 0x0000013933724b03 in Tcl_UtfToUniChar () from /usr/local/lib/libtcl85.so.1.8 #1 0x00000139336bf81c in Iso88591FromUtfProc () from /usr/local/lib/libtcl85.so.1.8 #2 0x00000139336c0517 in Tcl_UtfToExternalDString () from /usr/local/lib/libtcl85.so.1.8 #3 0x0000013899d2b0e5 in RTcl_ReadConsole () from /usr/local/lib/R/library/tcltk/libs/tcltk.so #4 0x000001390e5c13a6 in sigactionSegv () from /usr/local/lib/R/lib/libR.so.36.1 #5 0x00000138b4c92005 in ?? () :
落ちた時の状況視察。
(gdb) b sigactionSegv Breakpoint 1 at 0x205e57: file main.c, line 503. (gdb) r --gui=Tk Starting program: /home/sakae/MINE/lib/R/bin/exec/R --gui=Tk Program received signal SIGSEGV, Segmentation fault. 0x0000084567a5cb03 in Tcl_UtfToUniChar () from /usr/local/lib/libtcl85.so.1.8
soファイルの中ですねぇ。SEGVはまずgdbがキャッチしてくれるのね。
(gdb) c Continuing. Breakpoint 1, sigactionSegv (signum=11, ip=0x845f7039c30, context=0x845f7039b40\ ) at main.c:503 503 if(signum == SIGSEGV && (ip != (siginfo_t *)0) &&
継続するとやっとアプリに番が回ってきた。
=> if(R_ReadConsole("Selection: ", ConsoleBuf, CONSOLE_BUFFER_SIZE, 0) > 0) { if(ConsoleBuf[0] == '1') break; if(ConsoleBuf[0] == '2') R_CleanUp(SA_DEFAULT, 0, 1); if(ConsoleBuf[0] == '3') R_CleanUp(SA_NOSAVE, 70, 0); if(ConsoleBuf[0] == '4') R_CleanUp(SA_SAVE, 71, 0);
こういうの、中々お目にかかれない、貴重な体験ですよ。古くは、Cで書かれたLispを説明した本。数々のメジャーガベコレをくぐり抜けて(メジャーガベコレって引っ越し時に適用される)手元に残ってる。
あの頃は、SEGVを捉える機構なんて充実していなかったから、何かやる度にエラーは無いかといちいちチェックしてた。そのチェックがコードの大半を占めるようになるとコードが醜くなるので、マクロっぽい仕掛けを導入して、目立たないようにしてた。
static void init_signal_handlers(void) : sa.sa_sigaction = sigactionSegv; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_ONSTACK | SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGILL, &sa, NULL); #ifdef SIGBUS sigaction(SIGBUS, &sa, NULL); #endif signal(SIGINT, handleInterrupt); signal(SIGUSR1, onsigusr1); signal(SIGUSR2, onsigusr2); signal(SIGPIPE, handlePipe);
時代を経て、今ではこうなってる。佳からぬ事が起こったら、それを処理するルーチンを呼んでねって事だ。で、上記の登録は、R起動時に setup_Rmainloop()
の中で実行される。
楽なものだ。ああ、かの本はMSDOS用に書かれていたんで、そこまで洗練されてはいなかったのか。unixは偉大だなあ。
あれ? ユーザー割り込みが2つ登録されてるけど、どんな目的でつかってるのかな? 追ってみたら面白いだろうね。すっかりRの内部だな。
/* SIGUSR1: save and quit SIGUSR2: save and quit, don't run .Last or on.exit(). These do far more processing than is allowed in a signal handler .... */
最後の最後に、ご奉公しますって事か。わざわざソフトウェア割り込みにしてるのは、本体系が壊滅的な状態になっても、ご奉公出来るように独立させてるんだね。
with tcltk
以前作ったRはtcltkがサポートされていなかった。tcl/tkのportsが入っているので、configure時に自動認識してると思ったんだけど、ちと甘かったわい。先輩はどうしてるの。レシピを盗み見。こういうのリナではお手軽に出来ないので嫌い。まあ、タコは余計な事考えるなが大方針ですから。棲み分けするのさ。
CONFIGURE_ARGS= --disable-java \ --with-tcl-config=${MODTCL_CONFIG} \ --with-tk-config=${MODTK_CONFIG} CONFIGURE_ENV= FC="${MODFORTRAN_COMPILER}" \ FFLAGS="${FFLAGS}" \ CPPFLAGS="-I${LOCALBASE}/include" \ LDFLAGS="-L${LOCALBASE}/lib -Wl,-R${LOCALBASE}/lib/R/lib" \
大事な部分を暗記。でも、わけわかめな変数にはどんな値が入ってるの?
'--disable-java' '--with-tcl-config=/usr/local/lib/tcl/tcl8.5/tclConfig.sh' '--with-tk-config=/usr/local/lib/tcl/tk8.5/tkConfig.sh' 'CC=cc' 'CFLAGS=-O2 -pipe' 'LDFLAGS=-L/usr/local/lib -Wl,-R/usr/local/lib/R/lib'
こういう事もあろうかと、configure時の内容がMakeconfに残っていた。これで落着のはずなんだけど、突然現れた変数って、どこで定義されてるの? FreeBSD port 作成者のためのハンドブック とかを調べれば、何かヒントが有るかなあ?
とも角先に進む。下記はconfigのログの一部。
configure:44418: checking for /usr/local/lib/tcl/tcl8.5/tclConfig.sh configure:44448: result: /usr/local/lib/tcl/tcl8.5/tclConfig.sh configure:44493: checking for /usr/local/lib/tcl/tk8.5/tkConfig.sh configure:44523: result: /usr/local/lib/tcl/tk8.5/tkConfig.sh configure:44617: checking tcl.h usability configure:44617: cc -c -g -O2 -I/usr/local/include -I/usr/local/include/tcl8.5 conftest.c >&5 configure:44617: $? = 0 configure:44617: result: yes
そして、configの最後に、晴れ晴れしくtcltkが出現した。
Interfaces supported: X11, tcltk
これで作り直したRでは
> library(tcltk) > tkStartGUI()
こんな具合に途中からGUIにも切り替えられた。よかった。でも、なかなかCoreが出来るような事故は起きないなあ。
(gdb) bt #0 0x0000076ae61b4b03 in Tcl_UtfToUniChar () from /usr/local/lib/libtcl85.so.1.8 #1 0x0000076ae614f81c in Iso88591FromUtfProc () from /usr/local/lib/libtcl85.so.1.8 #2 0x0000076ae6150517 in Tcl_UtfToExternalDString () from /usr/local/lib/libtcl85.so.1.8 #3 0x0000076abc480d01 in RTcl_ReadConsole (prompt=<optimized out>, buf=0x768a91c89c0 <ConsoleBuf> "", len=4096, addtohistory=<optimized out>) at tcltk_unix.c:172 #4 0x00000768a9070103 in sigactionSegv (signum=0, ip=<optimized out>, context=<optimized out>) at main.c:623
暫く使っていたら、遭遇した。わーい、って喜ぶなよ。
hash
余り世間とかけ離れた事をやっていてもしょうがないので、前回見つけたハッシュについて調べてみる。先輩が語り尽くしていたんで、ホェーと驚く。
generate_random_labels <- function(num, length = 10) { apply( Vectorize(sample, "size")(letters, rep(num, length), replace = TRUE), 1, function(row) paste(row, collapse = "")) } generate_random_values <- function(labels, digits = 1) { format <- paste0("%s%0", digits, "d") sprintf( format, labels, Vectorize(sample, "size")(seq_len(10^digits-1), rep(1,length(labels)))) } N <- 1000 labels <- generate_random_labels(N) values <- generate_random_values(labels) df <- data.frame(labels, values, stringsAsFactors = FALSE) head(df)
よくこういう関数を見つけて来るものだ。
!> Vectorize(sample, "size")(letters, rep(3, 6), replace = TRUE) [,1] [,2] [,3] [,4] [,5] [,6] [1,] "v" "m" "q" "y" "c" "w" [2,] "k" "b" "l" "x" "z" "h" [3,] "p" "k" "f" "l" "g" "v"
> pi - 4*(4*atan(1/5) - atan(1/239)) [1] -4.440892e-16 > month.abb [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec" > month.name [1] "January" "February" "March" "April" "May" "June" [7] "July" "August" "September" "October" "November" "December" !> letters [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" " s" [20] "t" "u" "v" "w" "x" "y" "z"
Rは、こんな定数を持っているとな。
> sample(month.abb, 12) [1] "Oct" "Mar" "Nov" "Jun" "Dec" "Apr" "Feb" "May" "Aug" "Jul" "Jan" "Sep" !> sample(month.abb, 12) [1] "Aug" "Jan" "Dec" "Feb" "Mar" "May" "Sep" "Jun" "Jul" "Nov" "Oct" "Apr"
ランダムサンプリング。そう言えば、話題の国勢調査。回答率が余りに低いので20日までの延長戦になったな。650億円だかかけての、全数検査を目指していたけど、最終結果は如何に?
全数検査がかなわなかったら、抜き取り検査の様相になるな。統計を駆使して、全体を推測するんだろうね? それでいいのか >総務省。
強権発動して、未回答者は日本に居ないと見做し、国外追放ぐらいの荒技を繰り出せよ、と無茶言ってみる。そこまではしなくても、統計法違反で罰金100万円ぐらいを徴収すればよい。
R 4.0.3 on Debian
新しいの来てしまったら、入れる鹿。場所は古いRが入ってるdebian(64Bit)機。
sakae@pen:/tmp/$ head /opt/lib/R/etc/Makeconf # etc/Makeconf. Generated from Makeconf.in by configure. # # ${R_HOME}/etc/Makeconf # # R was configured using the following call # (not including env. vars and site configuration) # configure '--prefix=/opt' '--with-tcltk' '--disable-java'
何時もは、~/MINE/に入れるのだけど、今回は気分を変えて、/opt/の下にした。ぷちmacOS風?よう知らんけど。
例によってヘッダー無い攻撃をさかんに受ける。パッケージもrglを入れてみたんだけど、GL/xxx.h が無い攻撃を2度受けた。apt-file search GL/xxx.h と言う もぐらたたきゲームの開催ですよ。
configureの最後で、PDFなマニュアルは作成出来ひんでって警告を受けたけど、pdfデバイスはちゃんと機能してた。これが機能していなかったら、正直お手上げなんだけど、ちゃんと動いてくれて、ほっとしてる。