正月休み
実家に帰ったり、おせち食べたり、三社参りしたり、わりと普通の年末年始を楽しんだ。年末に崩した体調も回復した。ToDoリストの進捗は芳しからず。辞書ビューアをひと段落させたかったがコーディングはあまり進まなかった。
年をまたいだことだしサイトのトップページのリストを年単位でグループ分けすることにした。日記的なエントリを書きやすくなると思う。
ひそかに期待していたsteps to phantasienの更新はないらしい。休みの期間にちょくちょくチェックしていたのだけど残念。
2018年の振り返り
今年は Github の草を生やすのを日課にしていた。内容は二の次で些細な変更、例えばタイポの修正とかでもいいから毎日コードを書く。対象はほぼプライベートリポジトリ。
旅行してたり体調不良の時以外は埋まっている。習慣化できたのは良かった。ただ草をはやすのを目標にすると、難しいコーディングを避けて簡単なバグ修正をしたり、小さい検証コードを書いたりしがちであった。十分な時間を確保できないと、まとまったコードを書く気にならないのが問題だったと思う。来年はもうちょっと意味のあるコードを書くようにしたい。
あと学生だった時以来のブログを再開した。文章を書く練習をしたい、というのと学習したことを記憶に定着させたい、というのが動機。勉強したことを忘れないように記事にする、というのは nhiroki さんが言っていて、実際に効果あるなあと感じている。技術的な内容に限定する気はないので今後は日常の事とか趣味のこととか書いていきたい。shinh さんの日記みたいな、思ったことをふらっと書くスタイルとかやってみたいし、森田さんのブログみたいなちょっと詩的な感じのやつとか憧れたりする。
Rust
技術的なところでは今年一番時間を使ったのが Rust の勉強。本が 2 冊出たのと、Raph Levienが最近凝っているらしいというので勉強を続けている。自作の辞書ビューアを Rust で書き直したりしているが、まだ手になじまない。実際のアプリを作ろうとしたときに制約がきつくてつらい、イディオムがわからない、みたいな気持ちになる。Rust を学習すること自体は楽しいので来年も続けると思う。
LeetCode
コーディング面接対策で有名なサイト。面接で聞かれそうな問題が豊富にある。競技プログラミングは早々に挫折した身なのだけど、こちらは基礎的なアルゴリズムやデータ構造を知っていれば自分にも解ける問題が多い。動的計画法が苦手だという意識があったので 4 月 5 月あたりに集中的に解いていた。これまでのところ Solved は 113 個。今後もちょっとづつ解いていこうと思っている。
英語
昨年の 12 月から英会話スクールに通っている。ちょうど一年過ぎたぐらい。この間スクールが定めるレベルを一つクリアした。割と効果を実感していて、壊れた英語でもしゃべること自体の抵抗感がなくなった。受講料が高いのでコストパフォーマンスに見合うかどうかは正直わからないが、来年も続けようと思っている。あとひとつレベルをクリアしたい。
散歩とポッドキャスト
健康と趣味(町散策)を兼ねてできるだけ歩くようにしている。ここ数年は一日 10km ぐらい歩くのを目標にしていて、今年もだいたい達成できた。散歩しているときはいつもポッドキャストを聞いている。最近はポッドキャストの番組が増えてうれしい。今年よく聞いた番組たち:
- Misreading Chat
- mozaic.fm
- backspace.fm
- Freakonomics
- omoiyari.fm
- EM . FM
- ajitofm
- Rebuild
- 愚者の宮殿
- dex.fm
- engineer meeting podcast
- yatteiki.fm
写真
長いこと買うか悩んでいた Sony α7R III を冬前に購入した。写真撮るのが一段と楽しくなった。街撮りメインだけどポートレートとかも練習したい。
RustのSend
疑問: RustのライブラリAPIの定義を見ていたら制約にT: Send
と書いてあった。なぜこのAPIはSend
を要求するのか。
短い答え: そのAPIはスレッドをまたいで値を渡す。その値の所有権をスレッド越しに渡してもデータ競合を起こさない、ということを要求するため。Send
でない型はたいていRc<T>
かポインタ。
これはAPIドキュメントを見て疑問の思ったことを調べて、使う側からどう解釈すべきか、という視点から理解しようとするシリーズの二番目の記事となる。前回に引き続き、お題はthread::spawn
。自分の浅い理解から書いているので間違いを含んでいるかもしれない。
thread::spawn()
に渡すクロージャの制約はFnOnce() + Send +'static
なので前回の記事で言及したFnOnce()
に加えてSend
を実装していないといけない。APIドキュメントには値を安全に別スレッドに送るためにSend
が必要だ、と書いてある。でもRustには所有権の概念があるから値を渡せばそれで安全になるんじゃないのか。逆に言うとスレッド越しに渡すと安全じゃない値ってなんなんだろうか。調べた感じだと、大体二つに分類される。
ひとつはRc<T>
。Rc<T>
な値は参照カウンタとともにヒープ上に確保される。レイアウトはこんな感じ:
+--------------+------------+-------+
|strong ref cnt|weak ref cnt| value |
+--------------+------------+-------+
Rc::clone()
を呼ぶと値を複製できる。つまり所有権を持つ変数を複数作れる。Rc::clone()
はヒープ上の参照カウントを増やすのだけど、この参照カウンタはアトミック変数じゃないし、排他制御も行わない。これらの値を異なるスレッドで生成したりドロップしたりしたら参照カウンタのデータ競合が起こる。なのでRc<T>
はSend
ではない。
一方、Rc<T>
と同じように内部可変性(interior mutability)を使っているCell<T>
やRefCell<T>
は、T
がSend
であればCell/RefCellもSend
になる。Cell/RefCellがスレッド安全でなくなるのは、Cell/RefCellへの参照をスレッド間で渡したときであって、所有権を渡すこと自体は安全な操作となる。
もうひとつのSend
でない型はポインタ。*const u8
とか*mut u8
とか。これらがSend
ではない理由はRc<T>
ほど自明ではない。ポインタをスレッド間で渡すこと自体はデータ競合を起こさない。安全でなくなるのはポインタをdereferenceするときだけど、そのときはどのみちunsafeでくくらなければいけない。だったらポインタをSend
である、としても安全性は損なわないような…などと思いつつ調べていたらNomiconに言及があった。主に書き手に注意喚起する目的らしい。言語設計上の選択だったんだろう。
ちなみに参照&T
のほうはSend
を実装している。理由はコンパイラが借用ルールを使って安全でない参照の利用を検知できるから。
自分で定義した型はどうか。型がSend
であるかどうかは基本的にコンパイラがよしなに判断してくれる。内包する型が全部Send
を実装していれば、その型もSend
になる。内包する型のうち一つでもSend
でない型があれば、定義した型もSendを満たすことができない。
コンパイラにお任せできないこともある。メソッドをスレッド安全でない方法で実装したときなどがこれに該当する。この場合はimpl !Send for T
みたいにして明示的にSendを実装しない、と表明する必要がある。
thread::spawn()
に話を戻す。thread::spawn()
はクロージャを受け取る。API定義はそのクロージャがSend
を満たしていないといけない、と言っている。ではクロージャがSend
かどうかはどう決まるのか。それはクロージャの作り方によって決まる。クロージャがSend
でない値をキャプチャしていたら、そのクロージャはSend
ではない。ただし、Send
ではない値をクロージャの内部で使っていても、クロージャ自体はSend
になることができる。
// Error
fn f1() {
let v = Rc::new(42);
thread::spawn(move || {
let _v2 = Rc::clone(&v);
});
}
// OK
fn f2() {
thread::spawn(|| {
let _v = Rc::new(42);
})
}
API定義でSend
を見かけたときの心構えとしてはどう考えればいいか。ほとんどの型がSend
であることを考えれば、スレッド越しに値を渡すよということぐらいで特に気にしなくても良さそう、というのが今の理解。
余談だけど、スレッド安全に関するもう一つのトレイトとしてSync
がある。こちらはthread::spawn()
と直接関係がなかったのであまり追っておらず、理解があやふやなので言及を避けた。
Rust の Fn, FnMut, FnOnce の使い分け
Rustでスレッドを起動するにはthread::spawn()を使う。この関数は引数にクロージャをとるのだけど、引数の定義を見ると、 F: FnOnce() -> T + Send + 'static
となっていて初見では理解するのが難しい。ここではFnOnce()
、つまりクロージャの型について、どういう風に使い分けるのか、といった視点で見ていこうと思う。
Rustにはクロージャを表す型が3つある。
- Fn()
- FnMut()
- FnOnce()
型が3つ必要なのはRustが値の所有権や借用、生存期間をきちんと把握するためである。
Fn()
は使う側、引数として受け取る側としては使い勝手が一番よい。何度でも呼び出せるし、クロージャ自体が可変じゃなくてもいい。これは立場を変えると、クロージャを渡す側としてはFn()
は制約が一番厳しいことを意味する。Fn()
として渡されるクロージャは、何度実行されても所有権や借用のルールを破らないようにしなければならない。例えば、値の所有権をとってそれを消費するクロージャはFn()
としては渡せない。一度値を消費してしまったら二度は使えないから。以下のコードはコンパイルエラーで通らない。
fn do_something<F: Fn()>(f: F) {
f();
f();
}
fn main() {
let nums = vec![1,2,3];
do_something(move || {
for n in nums { ... } // Consume `nums`
});
}
FnOnce()
の使い勝手はFn()
の逆と言える。使う側としての制約は一番厳しい。一度しか実行できないし、実行にはそのクロージャの(借用ではなく)所有権が必要となる。一方、渡すクロージャの自由度は大きい。クロージャは一度しか呼ばれないことが保証されているので、そのクロージャ内で値を消費してもいいし、生存期間さえ守っていれば可変参照を取って値を変更することもできる。
fn do_something<F: FnOnce()>(f: F) {
f();
// Can't use `f` more than once
}
fn main() {
let nums = vec![0,1,2];
let mut greeting = "Hello".to_string();
let greeting_ref = &mut greeting;
do_something(move || {
for n in nums { ... } // Consume `nums`
greeting_ref.push_str(", World"); // Mutate value via mutable ref
});
println!("{}", greeting);
}
この流れで行くとFnMut()
はFn()
とFnOnce()
の中間的な立ち位置になる。値の消費はしないけれど可変参照経由で変更はする、みたいなクロージャはFnMut()
として取り扱うことができる。FnMut()
は何度も呼べるけれど、内部に可変参照をもっているのでそのクロージャを実行するにはmutを必要とする。
fn do_something<F: FnMut()>(mut f: F) {
f();
f();
}
fn main() {
let mut greeting = "Hello".to_string();
let greeting_ref = &mut greeting;
do_something(move || {
greeting_ref.push_str(", World");
});
println!("{}", greeting);
}
ここでthread::spawn()
に立ち戻ってみよう。ライブラリ関数としては利用方法にできるだけ制限をかけたくない。ライブラリ利用者側、つまりクロージャを渡す側にとって一番制約がゆるいのはFnOnce()
である。おそらくこれがspawn()
がFnOnce()
なクロージャを受け付けるようになっている理由だろう。spawn()
は渡されたクロージャを一度実行すればよいだけなので、利用者側の制約が厳しいFn()
を要求する必要はない。
自分がAPIを提供する立場だとしたら、引数として受け取るクロージャの型には注意しておきたい。ライブラリの中身を実装している最中に、コンパイラを通すためだけに安易にFn()
を要求するAPIを作ってしまうと、あとで利用する側で困る、みたいなことになりかねない。
ただ、thread::spawn()
と似たようなAPIを自作して提供する場合にはひとつ落とし穴がある。公式ドキュメントで言及されているので、自分で作ってみようとする前に目を通しておくとよい。自分はこれに悩んで時間を浪費してしまった。
追記
Send
について記事を書いた。
参考文献
Secure Shell App でウェブフォントを使う
Windows では ssh クライアントに Secure Shell App を使っているのだが、デフォルトの設定だと自分の4Kディスプレイではフォントの見栄えがよくない。ウェブフォントを使って自分の好みに変更してみる。
Secure Shell App のオプションページを開くと、user-css-text
という項目があって、ここに任意の CSS を書くことができる。ここで Google Fonts で提供されているフォントを@import
を使って取り込むようにする。今回は Roboto Mono を使うように設定した。
@import url("https://fonts.googleapis.com/css?family=Roboto+Mono:400");
さらに Secure Shell App の設定項目のfont-family
も更新しておく。
"Roboto Mono", Consolas, monospace
少し前に日本語の Noto Sans が Google Fonts で使えるようになったのでこちらも試してみたが、なんだか jaggedly であった。日本語に関してはメイリオの方ががよさそう。
Chromeのメモリ割り当てフック機構
Misreading Chat ep. 30はAddressSanitizer (ASAN)の話。operator new
の差し替えとかどうしているんだろうね、という話題に反応してみる。
Chromeのコードにもoperator new
を差し替えている場所はいくつかある。例えばUSING_FAST_MALLOCというマクロでアノテートされたクラスはPartitionAllocという独自アロケータを使ってoperator new
を実装している。このPartitionAlloc、レンダリングエンジンの内部では結構使っていて文字列やベクタ、ArrayBufferなんかにも使われている。PartitionAllocは内部でmmap
使っているんだけど、ASANがこの辺をチェックしてくれるのかどうか自分はよくわかっていない。
malloc
とかを差し替えたい動機としては、PartitionAllocみたいな最適化したメモリアロケータを使いたい、っていうのの他に、メモリの割り当てと解放をフックしたい、というのがある。これらをフックすればどういう風にメモリが使われているのかを調べることができる。例えば、よく作られるオブジェクト(Stringとか)の一部がすごく大きいのだけど、どこで作られているのか実行時でないと分からないとする。この場合はそのオブジェクトのnew
をフックしてスタックトレースを調べれば、どういうパスを通ってそのオブジェクトが作られたのかが分かる。
Chromeにはこのフックを入れる汎用的な仕組みがあって、Allocator shimと呼ばれている。この仕組を使ってヒーププロファイラやメモリトレースの機能が実装されている。Allocator shimはmalloc
とoperator new
を差し替えるので、ASANと共存することは残念ながらできない。
というわけで、ASANをネタにChromeのAllocator shimの紹介をしてみた。このあたりはドキュメントが充実しているので読んでみると面白いかもしれない。
夕立
午後六時半。九月も半ばを過ぎて辺りはもう真っ暗になっている。
先ほどから雷音と一緒に強い雨が降ってきた。あ、夕立だ、と思った。
夕立という言葉は季節感があって好きだ。最近よく耳にするゲリラ豪雨という言葉には無い含意がある。
夕立というには陽もとっくに暮れているけれど、自分にとってはこれは夕立。
Splatoon2 の漢字フォント
スプラトゥーン2は1に引き続きフォントを自作しているそうだ。
スプラトゥーンのイカしたUIはこうして作られた─担当デザイナーが語る秘話 | 超ゲームウォーカー!
ゲームによくマッチしているし、特徴的でかっこいいフォントだと思う。
自作といっても全部作るのはさすがに無理っぽくて、漢字は既存のフォントを使っているそう。
採用情報:仕事を読み解くキーワード - 世の中にないフォントを作る
じゃあ漢字部分のフォントは何だろう、と思って調べてみた。
スプラトゥーン2の公式アプリであるイカリング2にアクセスすると、Web フォントが二つロードされる。78 KBのフォントと 587 KBのフォント。サイズから言って後者のフォントが漢字を含んでいるフォントだろう。後者のメタデータを見ればフォント名が分かるんではないか。
サイズの大きいフォントをshowttf
で見てみる。NAME テーブルは見づらいので CFF テーブルのほうを見るとこんな感じだった。
...
Dump of top dictionary for KurokaneStd-EB
Version=0 .notdef
Notice=393 Copyright 2008-2013 Fontworks Inc. All Rights Reserved.
fullname=394 KurokaneStd-EB
familyname=395 KurokaneStd
weight=396 EB
...
というわけで、漢字の部分はフォントワークスの「くろかね EB」というフォントかな?
くろかね EB|書体見本|FONTWORKS | フォントワークス
ちなみに前者のフォントには「Splatoon2」という名前がついていた。ひねりなしの素直な名前。
最後にお約束事だけど、商用フォントの不正利用はご法度です。
世界時計 Web アプリ
ちょっと前から iPhone / Android みたいな世界時計の Web 版があるといいな、と思って作っている。
たまに海外に出ることがある。海外にいると大抵今日本は何時だっけ、ええと、となる。日本にいるときも海外のある都市の時間を知りたくなるときがある。
これまではスマホの世界時計を使って知りたい場所の時間を調べていた。だけど PC 上で作業しているときにスマホを取り出すのは PC 世代の自分には若干かったるい。ぱっとブラウザのタブを開いて興味のある都市の時間を調べたい。
要件定義は次の 3 つ。
- メインの時計は現在位置の時間を表示する。
- 任意の都市の現地時間を追加/削除できるようにする。追加する際は都市名で検索できるようにする。
- 追加した都市の時間は再度タブを開いたときも表示する。
1 は JS の API を使うだけ。3 もブラウザが提供する localStorage / IndexedDB で実装できる。2 に関しては Google Place API を使うことにした。
とりあえず欲しいものはできたけど、もう少しいじろうと思っている。レイアウトの調整と直感的でない操作の改善がひとつ。あとは React や Vue みたいなフレームワークを使ってみる、とか。
Emscripten Modularize
Emscripten で出力した JS/wasm を Web アプリの一部として組み込む場合、これらのリソースの読み込みをできるだけ遅延させたいことがある。例えばユーザーがあるボタンをクリックしたときのみ wasm が提供する機能を使いたいとする。ボタンが押されるまでは wasm の読み込みは避けたい。
Emscripten はデフォルトでは Web アプリに組み込んだり遅延ロードさせるにはあまり適さない JS/wasm を出力する。Module オブジェクトはシングルトンとして生成されるし、wasm の場所もランタイムの JS と同じパスにあると仮定されてしまう。後者はランタイムの JS を読み込む前に適宜 locateFile とかを設定すればよいのだけど読み込み順序に依存するコードはアプリに組み込むには使いづらい。
Emscripten のフロントエンドである emcc に -s MODULARIZE=1
を渡すと Web アプリに組み込みやすい形で JS/wasm を出力してくれるようになる。このオプションを渡すと Module はシングルトンオブジェクトではなくコンストラクタ関数として生成される。これにより JS/wasm の読み込みタイミングと Module オブジェクトの生成タイミングを分離できて、例えばランタイムの JS は他の JS とバンドリングしておいて、wasm のロードは後で行う、といったことが可能になる。
以下は -s MODULARIZE=1
で生成した wasm をボタンが押されたタイミングでロード/実行する例:
function onWasmBinaryReady(wasmBinary) {
return new Promise(resolve => {
let mod = null;
const args = {
wasmBinary: wasmBinary,
onRuntimeInitialized: () => {
resolve(mod);
}
};
mod = new Module(args);
});
}
const wasmPath = "a.out.wasm";
const button = document.getElementById("button");
button.addEventListener("click", () => {
fetch(wasmPath)
.then(res => res.arrayBuffer())
.then(buf => new Uint8Array(buf))
.then(wasmBinary => onWasmBinaryReady(wasmBinary))
.then(mod => {
// Call mod.ccall(...)
});
});