STM32F3DISCOVERYボード向けに実行プログラムをLDCで作ってみようとしてハマった
表題のとおり、STM32F3DISCOVERYボード向けに実行プログラムをLDCで作ってみようとしてハマった。
import ldc.attributes; extern(C): @nogc: nothrow: pragma(LDC_no_moduleinfo); @(section("_reset")) void _reset() { int y = void; auto x = 42; y = x; while (true) {} }
ENTRY(_reset); MEMORY { CCRAM : ORIGIN = 0x10000000, LENGTH = 8K FLASH : ORIGIN = 0x08000000, LENGTH = 256K RAM : ORIGIN = 0x20000000, LENGTH = 40K } SECTIONS { .text : { /* Vector table */ LONG(ORIGIN(RAM) + LENGTH(RAM)) LONG(_reset + 1); /* Reset handler */ _reset = .; *(.text._reset) *(.text*) } > FLASH /DISCARD/ : { *(.ARM.exidx*) *(.note.gnu.build-id*) } }
実行バイナリを作る手順はこのような感じ。
$ ldc2 -march=thumb -mcpu=cortex-m4 -c -g main.d $ arm-none-eabi-ld main.o -T layout.ld -nostartfiles -o main.bin
OpenOCDを起動する。
$ openocd -f board/stm32f3discovery.cfg Open On-Chip Debugger 0.9.0 (2015-09-02-10:42) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html adapter speed: 1000 kHz adapter_nsrst_delay: 100 Info : The selected transport took over low-level target control. The results m$ ght differ compared to plain JTAG/SWD none separate srst_only separate srst_nogate srst_open_drain connect_deassert_srst Info : Unable to match requested speed 1000 kHz, using 950 kHz Info : Unable to match requested speed 1000 kHz, using 950 kHz Info : clock speed 950 kHz Info : STLINK v2 JTAG v16 API v2 SWIM v0 VID 0x0483 PID 0x3748 Info : using stlink api v2 Info : Target voltage: 2.919073 Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
別ターミナルからGDBを使ってプログラムを実行しようとしたところ、以下のような状況になった。
$ gdb -q main.bin Reading symbols from main.bin...done. (gdb) target remote :3333 Remote debugging using :3333 warning: Architecture rejected target-supplied description 0x00000000 in ?? () (gdb) load Loading section .text, size 0x8 lma 0x8000000 Loading section _reset, size 0x1a lma 0x8000008 Start address 0x8000008, load size 34 Remote connection closed
OpenOCD側の出力はこのようになった。
Info : accepting 'gdb' connection on tcp/3333 undefined debug reason 7 - target needs reset adapter speed: 1000 kHz target state: halted target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x08000008 msp: 0x2000a000 adapter speed: 8000 kHz target state: halted target halted due to breakpoint, current mode: Thread xPSR: 0x61000000 pc: 0x2000003a msp: 0x2000a000 adapter speed: 1000 kHz target state: halted target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x08000008 msp: 0x2000a000 Error: gdb requested a non-existing register Info : dropped 'gdb' connection
gdb側でloadしようとするとconnection closedになってしまう。
$ arm-none-eabi-objdump -Cd main.bin main.bin: file format elf32-littlearm Disassembly of section .text: 08000000 <_reset-0x8>: 8000000: 2000a000 .word 0x2000a000 8000004: 08000009 .word 0x08000009 Disassembly of section _reset: 08000008 <_reset>: 8000008: b082 sub sp, #8 800000a: 202a movs r0, #42 ; 0x2a 800000c: 9000 str r0, [sp, #0] 800000e: 9001 str r0, [sp, #4] 8000010: e7ff b.n 8000012 <_reset+0xa> 8000012: 2000 movs r0, #0 8000014: 2800 cmp r0, #0 8000016: d102 bne.n 800001e <_reset+0x16> 8000018: e7ff b.n 800001a <_reset+0x12> 800001a: e7ff b.n 800001c <_reset+0x14> 800001c: e7f9 b.n 8000012 <_reset+0xa> 800001e: b002 add sp, #8 8000020: 4770 bx lr
readlelfでみるとsoft-float ABIになっている。
STM32F3DISCOVERYはCortex-M4Fなのでhardware floatにしなければないらないようだ。
$ arm-none-eabi-readelf -h main.bin ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: ARM Version: 0x1 Entry point address: 0x8000008 Start of program headers: 52 (bytes into file) Start of section headers: 1568 (bytes into file) Flags: 0x5000200, Version5 EABI, soft-float ABI Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 2 Size of section headers: 40 (bytes) Number of section headers: 16 Section header string table index: 13
float-abiを指定してみる。
$ ldc2 -march=thumb -mcpu=cortex-m4 -c -g -float-abi=hard main.d $ arm-none-eabi-ld main.o -T layout.ld -nostartfiles -o main.bin
結果変わらず。
$ arm-none-eabi-readelf -h main.bin ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: ARM Version: 0x1 Entry point address: 0x8000008 Start of program headers: 52 (bytes into file) Start of section headers: 1568 (bytes into file) Flags: 0x5000200, Version5 EABI, soft-float ABI Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 2 Size of section headers: 40 (bytes) Number of section headers: 16 Section header string table index: 13
LDCはtarget tripleの指定もできる。これが通って欲しい。けどだめそうだ。 軽く本体のソースを読んだ感じ、LDC側はthumbv7emやeabihfを解釈できないようだった。
$ ldc2 -mtriple=thumbv7em-unknown-none-eabihf -c -g main.d Targeting 'thumbv7em-unknown-none-eabihf' (CPU '' with features '') Error: target 'thumbv7em-unknown-none-eabihf' is not yet supported
$ ldc2 -march=thumb -mcpu=cortex-m4 -float-abi=hard -mabi=eabihf -c -g main.d
でもsoft-float ABIなバイナリが生成されてしまう。(上記にも書いたがそもそもeabihfをまともに解釈しないようだ)
上野動物園に行った
— けものフレンズをみて (@shitsyndrome) 2017年3月11日
上野動物園に行った。多摩動物公園よりは狭かったけど、数多くの動物がいた。
パンダ
人で賑わっていて辛かった。パンダも奥のほうでほとんど姿を見せず、おたかくとまった動物園のスターといった風情だ。とはいえ愛らしい感じはある。
アジアゾウ
多摩に比べて若々しく、よく動いていた。
インドライオン
見えにくい位置でずーっとじっとしていた。やる気をかんじない。
トラ
多摩はアムールトラだったが、上野にいたのはスマトラトラだった。近くまできてサービス精神旺盛だった。やっぱりトラかっこいいな。阪神は嫌いだけどトラは好きだ。
ゴリラ
思ったより小さいが、近くにくると迫力があった。
ミナミコアリクイ
ちょこまか動きまわって人間を舐め腐っている。
オグロプレーリードッグ
プレーリー式挨拶を間近でみることができた。かわいらしい外見だが、北米では害獣扱いで掃除機で吸われるらしい。
ホッキョクグマ
ずっと日陰でうずくまっていてやる気をかんじなかった。第三次産業を舐め腐っている。
ハシビロコウ
とても近くでみることができたので大満足。シャッター音に反応したのか、カメラを構えている人を攻撃しているところに遭遇できてとても幸運だった。
カバ・コビトカバ
コビトカバみて、カバってこんなもんなのか、と思っていたら本当?のカバはめっちゃでかかった。あれはすごい。。
クロサイ
ちょうど飼育員が餌を与えているところに遭遇した。サイはなんか好きだ。
イリエワニ
爬虫類館にいた。動かずとも近くでみると迫力がすごい。
総評
スマトラトラ・オグロプレーリードッグ・ハシビロコウ・ゴリラあたりは満足度が高かった。このあたりをウリにしていったらいいと思う。 ホッキョクグマはやる気がなさすぎる。
re-visit kNN and operf/LDC
以前書いたものはシングルスレッド版のRust/OCamlよりも遅かった。これをprofilingしてみる。
- スペック
$ lscpu [kubo39:knn][git:master] Architecture: x86_64 CPU 操作モード: 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 コアあたりのスレッド数:2 ソケットあたりのコア数:2 Socket(s): 1 NUMA ノード数: 1 ベンダー ID: GenuineIntel CPU ファミリー: 6 モデル: 69 Model name: Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz ...
- dmdのバージョン
$ dmd --version [kubo39:knn][git:master] DMD64 D Compiler v2.073.1
コードを2.073.1でも動くように修正。
import std.algorithm; import std.array; import std.conv; import std.range; import std.stdio; import std.string; import std.typecons; import std.parallelism; struct LabelPixel { int label; int[] pixels; } auto slurpFile(string filename) { int count; auto lp = File(filename) .byLine .dropOne .tee!(a => count++) .map!chomp .map!(a => a.to!string.split(",")) .map!(a => LabelPixel(a[0].to!int, a[1..$].to!(int[])) ) .array; return tuple(lp, count); } int distanceSqrt(const ref int[] x, const ref int[] y) pure { return reduce!((a, b) => a + (b[0] - b[1]) * (b[0] - b[1]))(0, x.zip(y)); } int classify(const ref LabelPixel[] training, const ref int[] pixels) pure { int smallest = int.max; int result = void; foreach (t; training) { int tmp = distanceSqrt(t.pixels, pixels); if (tmp < smallest) { smallest = tmp; result = t.label; } } return result; } void main() { const trainingSet = "trainingsample.csv".slurpFile; const validationSample = "validationsample.csv".slurpFile; int count(const LabelPixel data) pure { int num; if (classify(trainingSet[0], data.pixels) == data.label) num++; return num; } immutable num = taskPool.reduce!"a + b"( std.algorithm.map!(count)(validationSample[0]).array); writefln("Percentage correct: %f percent", num.to!double / validationSample[1].to!double * 100.0); }
timeコマンドで測ってみる。
- 最適化なし
$ time ./knn [kubo39:knn][git:master] Percentage correct: 94.400000 percent ./knn 110.16s user 0.03s system 99% cpu 1:50.33 total
うーん、遅い。
- 最適化あり
$ time ./knn [kubo39:knn][git:master] Percentage correct: 94.400000 percent ./knn 90.50s user 0.06s system 99% cpu 1:30.59 total
少し早くなったけどぜんぜん遅い。
Rustを見直し。
Rustのシングルスレッド版を再度測ってみる。
$ rustc --version [kubo39:knn][git:master] rustc 1.17.0-nightly (306035c21 2017-02-18)
少々手直しが必要。
use std::io::{BufRead, BufReader}; use std::fs::File; use std::path::Path; use std::str::FromStr; struct LabelPixel { label: i32, pixels: Vec<i32> } fn slurp_file(file: &Path) -> Vec<LabelPixel> { BufReader::new(File::open(file).unwrap()) .lines() .skip(1) .map(|line| { let line = line.unwrap(); let mut iter = line.trim() .split(',') .map(|x| i32::from_str(x).unwrap()); LabelPixel { label: iter.next().unwrap(), pixels: iter.collect() } }) .collect() } fn distance_sqr(x: &[i32], y: &[i32]) -> i32 { // run through the two vectors, summing up the squares of the differences x.iter() .zip(y.iter()) .fold(0, |s, (&a, &b)| s + (a - b) * (a - b)) } fn classify(training: &[LabelPixel], pixels: &[i32]) -> i32 { training .iter() // find element of `training` with the smallest distance_sqr to `pixel` .min_by_key(|p| distance_sqr(p.pixels.as_slice(), pixels)).unwrap() .label } fn main() { let training_set = slurp_file(&Path::new("trainingsample.csv")); let validation_sample = slurp_file(&Path::new("validationsample.csv")); let num_correct = validation_sample.iter() .filter(|x| { classify(training_set.as_slice(), x.pixels.as_slice()) == x.label }) .count(); println!("Percentage correct: {}%", num_correct as f64 / validation_sample.len() as f64 * 100.0); }
releaseビルド。めちゃくちゃ早い。。
$ cargo build --release [kubo39:knnrs][git:master] Compiling knnrs v0.1.0 (file:///home/kubo39/dev/dlang/knn/knnrs) Finished release [optimized] target(s) in 2.6 secs $ time ./target/release/knnrs [kubo39:knnrs][git:master] Percentage correct: 94.39999999999999% ./target/release/knnrs 1.21s user 0.01s system 99% cpu 1.217 total
D言語に戻る。
計測しやすいようにシングルスレッド版からはじめる。
import std.algorithm; import std.array; import std.conv; import std.range; import std.stdio; import std.string; import std.typecons; struct LabelPixel { int label; int[] pixels; } auto slurpFile(string filename) { int count; auto lp = File(filename) .byLine .dropOne .tee!(a => count++) .map!chomp .map!(a => a.to!string.split(",")) .map!(a => LabelPixel(a[0].to!int, a[1..$].to!(int[])) ) .array; return tuple(lp, count); } int distanceSqrt(const ref int[] x, const ref int[] y) pure { return reduce!((a, b) => a + (b[0] - b[1]) * (b[0] - b[1]))(0, x.zip(y)); } int classify(const ref LabelPixel[] training, const ref int[] pixels) pure { int smallest = int.max; int result = void; foreach (t; training) { int tmp = distanceSqrt(t.pixels, pixels); if (tmp < smallest) { smallest = tmp; result = t.label; } } return result; } void main() { const trainingSet = "trainingsample.csv".slurpFile; const validationSample = "validationsample.csv".slurpFile; int count(const LabelPixel data) pure { int num; if (classify(trainingSet[0], data.pixels) == data.label) num++; return num; } immutable num = reduce!"a + b"( std.algorithm.map!(count)(validationSample[0]).array); writefln("Percentage correct: %f percent", num.to!double / validationSample[1].to!double * 100.0); }
シングルスレッド版のほうが早い。といってもあまり変わらないが。
$ dmd -O knn.d [kubo39:knn][git:master] $ time ./knn [kubo39:knn][git:master] Percentage correct: 94.400000 percent ./knn 88.90s user 0.02s system 99% cpu 1:28.94 total
dmdだとだめそう。ldc2は理論値性能が出る?
http://leonardo-m.livejournal.com/111598.html
$ ldc2 -version | head -2 LDC - the LLVM D compiler (1.0.0): based on DMD v2.071.2 and LLVM 3.8.1
最適化つきで計測。DMDよりぜんぜん速いけどRustより遅い。
$ ldc2 -O knn.d $ time ./knn Percentage correct: 94.400000 percent ./knn 10.76s user 0.02s system 99% cpu 10.794 total
operf
operfはdwarf情報を使うので-gつけてビルド。
$ ldc2 -O -g knn.d $ sudo operf ./knn operf: Profiler started Percentage correct: 94.400000 percent Profiling done.
opannotate --source
をみると、distanceSqrt関数が14%を占めていることがわかる。
$ opannotate --source [ ... ] /* * Total samples for file : "/home/kubo39/dev/dlang/knn/knn.d" * * 45735 15.2286 */ :import std.algorithm; :import std.array; :import std.conv; :import std.range; :import std.stdio; :import std.string; :import std.typecons; : :struct LabelPixel :{ : int label; : int[] pixels; :} : :auto slurpFile(string filename) :{ : int count; : : auto lp = File(filename) : .byLine : .dropOne : .tee!(a => count++) : .map!chomp : .map!(a => a.to!string.split(",")) : .map!(a => LabelPixel(a[0].to!int, a[1..$].to!(int[])) ) : .array; : return tuple(lp, count); :} : :int distanceSqrt(const ref int[] x, const ref int[] y) pure :{ 45028 14.9931 : return reduce!((a, b) => a + (b[0] - b[1]) * (b[0] - b[1]))(0, x.zip(y)); /* _D3knn12distanceSqrtFNaKxAiKxAiZi total: 165424 55.0818 */ :} : :int classify(const ref LabelPixel[] training, const ref int[] pixels) pure :{ : int smallest = int.max; : int result = void; : 523 0.1741 : foreach (t; training) : { 33 0.0110 : int tmp = distanceSqrt(t.pixels, pixels); 50 0.0166 : if (tmp < smallest) : { : smallest = tmp; : result = t.label; : } : } : return result; :} : :void main() :{ : const trainingSet = "trainingsample.csv".slurpFile; /* _Dmain total: 711 0.2367 */ : const validationSample = "validationsample.csv".slurpFile; : : int count(const LabelPixel data) pure : { : int num; 101 0.0336 : if (classify(trainingSet[0], data.pixels) == data.label) : num++; : return num; : } : : immutable num = reduce!"a + b"( : std.algorithm.map!(count)(validationSample[0]).array); : : writefln("Percentage correct: %f percent", : num.to!double / validationSample[1].to!double * 100.0); :}
distanceSqrtの実装を変えてみる。
... int distanceSqrt(const ref int[] x, const ref int[] y) pure { int total; foreach (i, a; x) total += (a - y[i]) ^^ 2; return total; } ...
4倍以上早くなった。
$ ldc2 -O knn.d $ time ./knn Percentage correct: 94.400000 percent ./knn 2.45s user 0.01s system 99% cpu 2.463 total
その他シングルスレッド版では無駄な処理を省いてみる。
import std.algorithm; import std.array; import std.conv; import std.range; import std.stdio; import std.string; import std.typecons; struct LabelPixel { int label; int[] pixels; } auto slurpFile(string filename) { int count; return File(filename) .byLine .dropOne .map!chomp .map!(a => a.to!string.split(",")) .map!(a => LabelPixel(a[0].to!int, a[1..$].to!(int[])) ) .array; } int distanceSqrt(const ref int[] x, const ref int[] y) pure { int total; foreach (i, a; x) total += (a - y[i]) ^^ 2; return total; } int classify(const ref LabelPixel[] training, const ref int[] pixels) pure { int smallest = int.max; int result = void; foreach (t; training) { int tmp = distanceSqrt(t.pixels, pixels); if (tmp < smallest) { smallest = tmp; result = t.label; } } return result; } void main() { const trainingSet = "trainingsample.csv".slurpFile; const validationSample = "validationsample.csv".slurpFile; immutable num = validationSample .filter!(a => classify(trainingSet, a.pixels) == a.label) .count; writefln("Percentage correct: %f percent", num.to!double / validationSample.length.to!double * 100.0); }
うーむ、誤差程度。というか少し遅くなってる。
$ ldc2 -O knn.d $ time ./knn Percentage correct: 94.400000 percent ./knn 2.52s user 0.00s system 99% cpu 2.528 total
もう一度operfをかけてみる。72%が二乗の処理らしい。
... :int distanceSqrt(const ref int[] x, const ref int[] y) pure :{ : int total; 8513 13.5856 : foreach (i, a; x) 45571 72.7251 : total += (a - y[i]) ^^ 2; : return total; :} ...
operf –assemblyもしてみたがあまりわからなかった。
sub -> imul -> add -> inc の場所で60%以上喰ってるらしいが。。
000000000001a580 <_D3std9algorithm9iteration62__T12FilterResultS213knn4mainFZ9__lambda1TAxS3knn10LabelPixelZ12FilterResult8popFrontMFNaZv>: /* _D3std9algorithm9iteration62__T12FilterResultS213knn4mainFZ9__lambda1TAxS3knn10LabelPixelZ12FilterResult8popFrontMFNaZv total: 54369 86.7655 */ ... 4071 6.4968 : 1a600: cmp %rsi,%rbx 21 0.0335 : 1a603: jae 1a649 <_D3std9algorithm9iteration62__T12FilterResultS213knn4mainFZ9__lambda1TAxS3knn10LabelPixelZ12FilterResult8popFrontMFNaZv+0xc9> 3182 5.0780 : 1a605: mov (%rcx,%rbx,4),%r8d 14565 23.2438 : 1a609: sub (%rdx,%rbx,4),%r8d 6849 10.9301 : 1a60d: imul %r8d,%r8d 10065 16.0624 : 1a611: add %r8d,%ebp 9923 15.8358 : 1a614: inc %rbx 5268 8.4070 : 1a617: cmp %rax,%rbx 35 0.0559 : 1a61a: jb 1a600 <_D3std9algorithm9iteration62__T12FilterResultS213knn4mainFZ9__lambda1TAxS3knn10LabelPixelZ12FilterResult8popFrontMFNaZv+0x80> ...
そもそもなんでRustこんなはやいのか。
objdumpでみてみる。なるほど、SIMDでループをベクトル化してるのか。releaseビルドぱない。
// run through the two vectors, summing up the squares of the differences x.iter() .zip(y.iter()) .fold(0, |s, (&a, &b)| s + (a - b) * (a - b)) 8b80: f3 0f 6f 0c b7 movdqu (%rdi,%rsi,4),%xmm1 8b85: f3 0f 6f 14 b2 movdqu (%rdx,%rsi,4),%xmm2 8b8a: 66 0f fa ca psubd %xmm2,%xmm1 8b8e: 66 0f 70 d1 f5 pshufd $0xf5,%xmm1,%xmm2 8b93: 66 0f f4 c9 pmuludq %xmm1,%xmm1 8b97: 66 0f 70 c9 e8 pshufd $0xe8,%xmm1,%xmm1 8b9c: 66 0f f4 d2 pmuludq %xmm2,%xmm2 8ba0: 66 0f 70 d2 e8 pshufd $0xe8,%xmm2,%xmm2 8ba5: 66 0f 62 ca punpckldq %xmm2,%xmm1 8ba9: 66 0f fe c8 paddd %xmm0,%xmm1 8bad: f3 0f 6f 44 b7 10 movdqu 0x10(%rdi,%rsi,4),%xmm0 8bb3: f3 0f 6f 54 b2 10 movdqu 0x10(%rdx,%rsi,4),%xmm2 8bb9: 66 0f fa c2 psubd %xmm2,%xmm0 8bbd: 66 0f 70 d0 f5 pshufd $0xf5,%xmm0,%xmm2 8bc2: 66 0f f4 c0 pmuludq %xmm0,%xmm0 8bc6: 66 0f 70 c0 e8 pshufd $0xe8,%xmm0,%xmm0 8bcb: 66 0f f4 d2 pmuludq %xmm2,%xmm2 8bcf: 66 0f 70 d2 e8 pshufd $0xe8,%xmm2,%xmm2
多摩動物公園に行った
多摩動物公園に行った。
サーバル
お目当てのサーバルキャット。耳はけっこう個体差があるようだ。 あまり人間は好きでないのだろうか、やや人から離れた場所をうろついていた。
ライオン
運良くめちゃくちゃ吼えてるとこに遭遇した。ライオンの咆哮はめちゃくちゃ迫力がある。 目の前で喧嘩もはじまっておしっこちびった。勝てる気がしない。 それでも大半の時間はゆったり過ごしているようだ。
トラ
多摩動物公園で飼育されているのはアムールトラであった。こいつはクマとかも餌にする。つまり勝てない。 トラは室内飼育用の檻と観察用の柵から観ることができた。ボール遊びをしている姿は、やっぱりネコの仲間なのだなあという風情であった。
チーター
ぐるぐる動き回っていた。他の大型肉食獣と比較すると小型だろうが、それでも思っていたより大きくて勝てそうな気はしなかった。 人間には興味があるらしいのか、こちらの様子は常に伺っていた。
ユキヒョウ
チーターよりはひとまわり大きい。まあ勝てないな。結構サービスをしてくれて、目の前で吊るされた丸太に飛びついたりしていた。 ヒョウはちょうど先日テレビで都市との共存というテーマで題材になっていた。ムンバイでは都市部に出没し、家畜を襲うらしい。 近くに「手を出すとかじるよ!」という看板があったけどかじるだけですむのかな?
オオカミ
遠吠えを聞きつけて急行すると、タイリクオオカミがいた。オオカミは4匹の1チームでブースをぐるぐる回っていた。群れるのは卑怯なので勝てない。 何度か目があったので、そこそこ人間に興味はあるようだ。思っていた以上に犬、というかんじだった。 室内飼育のほうでは激しい爪とぎをしていたり、重低音なうなり声をあげていた。一匹でも勝てそうにない。 やはりオオカミなのだな。はじめてみたけど。
ワシ
ワシはけっこう多様な種類がいた。とにかく見た目がかっこいいしワシに生まれた時点で勝ちでしょ。
フクロウ
昼でも活動しているようだった。猛禽類だけあってけっこう大きい。
タスマニアデビル
かなり活発に動いており、可愛らしい見た目もあいまってとても人気があるようだ。実際活動している動物をみるのは飽きない。 タスマニアデビルは顔に腫瘍ができる病気で絶滅しかけているらしい。動物はいろいろな理由で絶滅の危機に瀕している。
カンガルー
やる気がなかった。ブースには糞がたくさんあった。
アフリカゾウ
とにかくでかい。象は賢い動物のようで、検査?のときも自ら進んで脚をあげて協力していた。
インドゾウ
糞を鼻でつかんで人間を攻撃しようとしていた。
キリン
わりと穏やかな生物なのか、実際に触っている人もいた。
クロサイ
なかなか迫力があった。鎧のような皮膚をしている。皮が余っていて垂れ下がっているのだが、あれはいったいなんなのだろうか。 どうでもいいが子どものころ「かんちがい」という絵本が好きだったのだが、あれはサイの親子が出てくる絵本だった。
松江周辺のうまい店
ずいぶん戻っていないが、青春時代の記憶を頼りにまとめた。
- おおはかや - 鰻のお店。アクセスは悪いけどめちゃくちゃ美味しい。
- 海鮮問屋博多 - ちょっと場末の香りがする、海鮮系の料理のお店。郷土料理のかにまま、鯛ままがおいしい。
- きがるそば - 松江でおいしいそばと言えばここ。おすすめはやっぱり割子そば。
- かつふじ - 松江でとんかつはここがいいだろう。かつももちろん豚汁もかなりおすすめできる。
- カフェ ブラン - 洋食。隠れ家的なところにある。若鶏の香草パン粉焼きマスタード風味を食べただけだが、他のメニュー全般的に高評価。
- CAFFE VITA - 全国バリスタ選手権優勝経験者の店。カフェオレボウルで飲むカフェオレが絶品。ちなみにこの店のオーナーの兄も安来に店をかまえており、こちらは世界バリスタ選手権で準優勝した腕前の持ち主。
- 珈琲館 - ケーキと喫茶店の店。「ウィーンの森」は私が知る限り最高のチョコレートケーキ。
- リビドー - 松江はケーキも名店が多いが、ただひとつおすすめするとしたらここだろうか。種類も多く食べ比べるのも一興。
この他、ケーキや喫茶店は隠れた名店揃いだ。また境港まで出れば新鮮な魚介類にもありつける。
各OSにおけるRustのインストール方法
たまーに聞かれるので。うろ覚えで書いているので違ってたら直すのでこっそり教えてください。
Linux/macOS/FreeBSD/Windows
rustup使いましょう。macOSでもhomebrew使うよりrustup使ったほうがいいと思います。.bashenvなり.zshenvなりに export PATH="$HOME/.cargo/bin:$PATH"
を追記してください。
NetBSD
rustupで入れられるように見えたんですが、つい最近試した時rustupで入りませんでした。packagesにバイナリがあるのですが1.9.0とけっこう古いです。stable/nightly新しいものを使いたければ自前でビルドするほかなさそう?
OpenBSD
こちらはそもそもrustupでサポート対象にされているものではなく、packagesにバイナリもおかれていません。portsのlang/rustに1.10が置かれているので多少古くてもよいならこれをビルドするのが一番楽でしょう。新しいのがほしければこちらも依存を調べてビルドするしかなさそうです。
DragonflyBSD
こちらもrustupは対応していませんが、意外にもpackagesに1.14.0と現時点で最新のstableのバイナリが置かれているので人権があります。
DMD on FreeBSD
だいたいこの記事のとおりなんだけど、細々と補足をしておく。
まず選定したFreeBSDのboxの話。最初はfreebsd/FreeBSD-11.0-STABLEのboxを試していたのだけど、これは毎度VMが突然シャットダウンしてしまって使い物にならなかったためにfreebsd/FreeBSD-11.0-RELEASE-p1を使うことになった。ログも一応目を通したが原因はさっぱり不明。これはけっこう時間を喰ってしまった。
いったんFreeBSDを入れてしまうと案外楽に環境構築はできた。記事中のbash/gcc/curlもpkgでバイナリで手軽にインストールできた他、gitやzsh、emacs-nox11もあっさりバイナリインストールして既存のLinux環境とだいたい同じような環境を構築できた。まったく同じというわけにはいかずGNU toolとBSD toolにはオプションの差異があるが(lsやnmなど)、そこはmanを見ながら地道に解決していくしかない。
portsのセットアップもNetBSDやOpenBSDに比べるとずっと簡単で、だいたいここの通りにやれば解決する。というかOpenBSDはportsのmakeがX11に依存してるのおかしいでしょ、X11がなんか壊れたのでportsが機能しなくなってしまったんだよなあ。
D言語環境の構築は公式の提供しているバイナリインストーラを使ったのは元記事に追記しているとおりportsのlang/dmd2がやや古いバージョンであったため。ていうか最新のportsにはまだlang/dmd1が入ってる。最新版のdmd-2.073.0に比べ2017年1月27日時点でのlang/dmd2は2.071.1であった。バイナリインストーラは引数にldcやgdcも受け付けられるので他のコンパイラもFreeBSDで簡単に動かせるかもしれない。
ところでこの公式インストーラで入るdub、libphobos2.072.2.soに動的リンクしているため、さらにdmd-2.072.2をダウンロードしてdmd-2.073.0より先にactivateするようにして運用している。これはバグなんだろうか。
D言語環境を構築したので https://github.com/kubo39/syscall-d とか https://github.com/kubo39/tiny-d-demo といったプログラムを試しにFreeBSD上でも動くように修正してみた。このあたりはわりと簡単に動かせる状態にできた。