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

余り世間とかけ離れた事をやっていてもしょうがないので、前回見つけたハッシュについて調べてみる。先輩が語り尽くしていたんで、ホェーと驚く。

R でハッシュテーブルの速度比較

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デバイスはちゃんと機能してた。これが機能していなかったら、正直お手上げなんだけど、ちゃんと動いてくれて、ほっとしてる。


This year's Index

Home