Webブラウザで使えるいろんな処理系
CMU #36で「Webブラウザで使えるいろんな処理系」というタイトルでLTをしてきました。
発表者数20人以上で、クオリティも非常に高い満腹会でした。
Boost.勉強会 #19 東京 に参加してきました
C++で開発する割に、C++系の人と滅多に会わないので初めて参加してきました。
厳つい老齢の開発者にコテンパンにのされるのかとビクビクしていましたが、July Tech Festaより若い人が多い印象です。 内容は低レイヤーや規格、Boostの実装についての物が多いです。
Boostライブラリ一周の旅 1.59.0-1.60.0 / Boost.Configについて
- BoostライブラリのコミッタやC++の規格策定に関与している方が何人か参加している(っぽい)。
- C++でも一時期のWebブラウザ程ではないが、コンパイラ間の差異に悩まされる。Boostのような幅広いライブラリの開発においてはクリティカルな問題。
EMC++とCppCoreGuidelinesについて
- Effective Modern C++とC++ Core Guidelinesについては、とにかく一度読んだ方が良い。ただし、ガイドラインに沿うことが目的ではない。ガイドラインに沿わない場合に、「なぜこう書く必要があるのか」を考えることが重要。
- 静的解析でガイドラインに沿っているかある程度の検知が可能。(https://github.com/Microsoft/GSL)
512bit SIMD (AVX-512)
- SIMDはコンパイラにお任せ。人力実装でコンパイラより早くするのは至難の業。
- 環境依存になりやすく、他の手もやりつくし、残るはSIMDの人力実装のみ且つ失敗(性能向上しない)としても試すほかない環境以外では手を出すべきではない。
「女性のためのC++コミュニティ Ladies++ meetup #1」の紹介
クロスプラットフォームマルチメディアライブラリSDL2の紹介
- SDL2。Emscripten, iOS, Android上でも動く。SteamのマルチプラットフォームタイトルはSDLを使っているものがある。
- どうもEmscripten上でSDLやOpenGLを使う人がそこそこ居る(ゲーム関係?)。
- SDL2からはIMEが使えるようになったと聞いたものの、調べてみたら完全対応という訳ではないっぽい。Electronなどと合わせて、GUI部品はwebで作り、エンジン部分がSDLのような住み分けはどうだろう?とおもったが、V8のasm.js対応ってどうなっていたっけ(パフォーマンス的意味で)?
クソザコ鳥頭が非順序連想コンテナに入門してみた
- 非順序連想コンテナの実装について、概念図はあくまで概念図で実装はパフォーマンスやメモリ重視でかな〜〜り工夫されている。
- libstdc++の実装には計算オーダー的にグレーな実装がされている場合がある。
- どうも、この界隈のクソザコとは、ライブラリの実装を追い、幾つかの実装の比較ができるようになってから名乗ることが許される称号のようだ。
expectedによるエラーハンドリング
- 毎回頭を悩ませるエラーハンドリングの新しい仕組みについて。
- まだ制式採用されたわけではないので利用できないが、やりたいことは理解できた。
メモリモデル再入門
- 変数とは何?マルチスレッドでのロックの話。
- バグを出したくなければData Race Freeなコードを書け。
- 2スレッドでtryLockに失敗したからといって、スレッド1でロックが成功しているわけではない。
- 静的解析で指摘してくれる(google/sanitizers · GitHub)。
ほか、懇親会や休憩時間で聞けたこと
- Emscriptenの例外対応はONにするとパフォーマンスが落ちるのでデフォルトOFFになっている(ヤベ)。
- libuvのようなcallback登録を行うライブラリとC++の例外の扱いについて実装のヒントをもらえた。
- armプロセッサはx86系に比べメモリバリアを厳としないとData Race時に異常値を持ってクラッシュしやすい。
- x86上で動くarmエミュレータはホストのメモリに守られるため実機のみで再現する厄介なバグに遭遇する可能性がある。
- Boostのテスト環境はボランティアによるもの。CPUアーキテクチャ×コンパイラバージョン×OS他の環境による組み合わせ数の爆発でかなり大変。
- マルチスレッド、ロックフリーで使える汎用アルゴリズム集libcds
- アムダールの法則
OSXでループバックデバイスっぽいことをする
OS Xではあまり馴染みがないかもしれませんが、ループバックデバイスっぽいことができます。
前提
今回入れたもの
- FUSE for OS X 2.8.2
- fuse-ext2 0.0.7
- e2fspros 1.42.13
- archivemount 0.8.3
ext4を内包するファイルを使ったループバック
OSXだとデフォルトでHFS(+)ですが、後からサイズ変更する方法がわからなかったので、資料があるext系のファイルシステムで試しました。 FUSE for OS Xは以下のサイトからインストーラをダウンロードしてインストールします。
途中、インストールするコンポーネントの選択画面が出ます。MacFUSE Compatibility Layerにチェックを入れます。
fuse-ext2も同様に以下のサイトからインストーラをダウンロードしてインストールします。
extを操作するためのコマンドラインツールをインストールします。
$ brew install e2fsprogs
適当なサイズの入れ物を作る。見ての通り小さすぎるとジャーナルできないらしい。 ブロックサイズやinodeの数は自動的に計算されますが、後から変更できないっぽいので、サイズやinode数は適宜調整します。 今回はとにかく作ったものです。
最初にext4のファイルシステムを内包した空のファイル(image.bin)を作ります。
$ cd <作業フォルダ, イメージ格納先> $ gtruncate -s 1MB image.bin $ <e2fsprosインストール先(/usr/local/Cellar/e2fspros/あたりなはず)>/sbin/mkfs.ext4 -F image.bin mke2fs 1.42.13 (17-May-2015) Filesystem too small for a journal Creating filesystem with 976 1k blocks and 128 inodes Allocating group tables: done Writing inode tables: done Writing superblocks and filesystem accounting information: done
作ったファイルをマウントして使います。
$ mkdir <マウント先> $ fuse-ext2 -o rw+ image.bin <マウント先> $ ls volume lost+found
lost+foundは消しても良いはずです(ファイルシステムを作るときに見つかったデータとかだったと思う)。
もっとサイズが必要になったら拡張ができます。
$ umount <マウント先> $ <e2fsprogs>/sbin/resize2fs image.bin 50M resize2fs 1.42.13 (17-May-2015) Resizing the filesystem on image.bin to 51200 (1k) blocks. The filesystem on image.bin is now 51200 (1k) blocks long. $ fuse-ext2 -o rw+ image.bin <マウント先>
圧縮ファイルをマウントする
領域サイズを調整するのが面倒だし、普段使わないので多少スピードが遅くなっても容量を節約したい場合、archivemountを使うと、圧縮ファイルtar.gzをマウントできるようになります。書き換えもできます。
$ brew install homebrew/fuse/archivemount $ touch dummy $ tar czvf image.tar.gz dummy $ rm dummy $ archivemount image.tar.gz <マウント先>
使い終わったらアンマウントすると圧縮ファイルの内容が更新されます。
$ umount <マウント先>
リンク先にあるように、archivemountはアンマウント時に圧縮ファイルが2つできます。そのため十分なファイルサイズが必要になります。-o nobackupオプションでバックアップが自動削除されますが、一時的には2つ分の容量を消費するようです。
ライブラリをコンパイルしてLLVM-IRを出力する方法
皆さんご存知、LLVM-IRはコンパイラ基盤であるLLVMの中間生成物であり、emscriptenやPROCESS WARPはLLVM-IRを読み込んで動かすことができるプラットフォームです。 現代のプログラミングでは既存ライブラリを利用することにより開発期間の短縮や品質向上を図っています。前述のプラットフォームでプログラムを動かす場合、自分のプログラムをLLVM-IRに変換するわけですが、その過程で依存ライブラリも同様にLLVM-IRに変換する必要があります。
$ clang -emit-llvm -c hoge.c
上記コマンドでC/C++からLLVM-IRへの変換はできていましたが、autoconfなどのビルドツールでは同様の方法では対応できませんでした。このような場合には-fltoオプションを使うとうまくいくようです。
The LLVM gold plugin — LLVM 3.8 documentation
GMPというライブラリを例にLLVM-IRの塊を取り出してみます。 GMPはC/C++で任意精度の整数、小数演算を行うためのライブラリです。標準C/C++ライブラリ以外にほとんど依存しません。
前提
- OS X Yosemite Version 10.10.5
- clang Apple LLVM version 7.0.0 (clang-700.1.76)
- LLVM 3.6.2 (homebrewでインストル済み)
- GMP 6.1.0 (.tar.bz2ファイルをダウンロード、解凍しておく)
コンパイル作業
$ cd <gmpの展開先> $ CC="clang -flto" CXX="clang++ -flto" ./configure --disable-shared <省略> $ make <省略>
GMPは通常、共有ライブラリと静的ライブラリの両方をコンパイルします。LLVM-IRを取り出す場合、静的ライブラリだけあればOKなので--disable-sharedオプションを指定しています。 GMPの場合、.libフォルダ以下にコンパイル済みライブラリが格納されています。
$ cd .lib $ ls libgmp.a libgmp.la libgmp.lai
静的ライブラリlibgmp.aにはLLVM-IRのBitCodeが含まれています。一旦arを展開し、中のBitCodeを取り出します。
$ mkdir work $ cd work $ llvm-ar x ../libgmp.a $ ls <libgmp.aに格納されていた.oファイルが展開されているはず>
展開された.oファイルはただのオブジェクトファイルではなくBitCodeです。 llvm-disコマンドでヒューマンリーダブルな.llファイルに変換できます。
$ find *.o -exec llvm-dis {} \;
あとはllvm-linkコマンドで1つにまとめて終わりです。
$ llvm-link -o ../libgmp.bc *.ll $ cd .. $ llvm-dis libgmp.bc $ ls libgmp.a libgmp.bc libgmp.la libgmp.lai libgmp.ll work
失敗談
fltoオプションの存在に気づくまで、emscriptenのようにCCにラッパを流し込んでなんとかしようとしていました。 基本動作は以下のとおりでスクリプトを組んだのですが、autoconfの関数有無の判定はプログラムがリンクまで正常に行えるかを基準にしています。 LLVM-IRの出力までで処理を止めた場合、関数がなくともリンク相当のコマンドが正常終了し、存在しない関数が有ると判定しmakeで止まるとうい問題が発生しました。 その時の調査で-fltoオプションを見つけて無事ライブラリのLLVM-IRを出力できることが分かりました。 ここまで来るのに2日ほどかけてgcc/clang/emcc/autoconfの動作をトレースしたんですけどね… 以下は必要なくなったラッパプログラムです。
#!/usr/bin/env python # coding:utf-8 import subprocess import sys import re import os import stat i_name = False o_name = False o_name_idx = False is_c = False is_e = False is_o = False is_s = False s_idx = False # 起動引数を読み取って、-c, -o, -S関連の有無と場所を確認 for idx, arg in enumerate(sys.argv): if is_o == True and (not o_name): o_name = arg o_name_idx = idx matched = re.match(r"^(.*)\.(c|i|cpp|cxx|cc|c\+\+|ii|s)$", arg) if matched: i_name = matched.group(1) if arg == "-c": is_c = True if arg == "-E": is_e = True if arg == "-o": is_o = True if arg == "-S": is_s = True s_idx = idx # 起動引数をコピーしてコマンドを作成 command = sys.argv[:] # command[0] = "clang" # -Sオプションの場合は-cで上書きする if is_s: command[s_idx] = "-c" # プリプロセッサ出力の場合を除き、出力形式に合わせて出力ファイル名を決定 if not is_e: if is_o: # 実行可能形式でのコンパイルをしようとした場合は、hoge.00に出力 if not is_c and not is_s: command[o_name_idx] = o_name + ".00" else: command.insert(1, "-o") if is_s: # アセンブラやオブジェクトファイルの場合は # .s, .oファイルを出力(中身はLLVM-IR) command.insert(2, i_name + ".s") elif is_c: command.insert(2, i_name + ".o") else: # 実行可能形式でファイル名が指定されない場合はa.out.00に出力 o_name = "a.out" command.insert(2, o_name + ".00") # コンパイルオプションに-emit-llvmを付加する command.insert(1, "-emit-llvm") command.insert(2, "-c") command.insert(3, "-fno-vectorize") # set target command.insert(1, "-m64") proc = subprocess.Popen( command, shell = False, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) stdout_data, stderr_data = proc.communicate() print stdout_data print stderr_data # 実行可能形式でのコンパイルの場合、LLVM-IRを # 実行するクッションスクリプトを作成 # @todo リンクオプションをlliに渡すようにする if not is_c and not is_e and not is_s: f = open(o_name, "w") f.write("#!/usr/bin/env sh\n") f.write("/usr/local/Cellar/llvm/3.6.2/bin/lli " + o_name + ".00 \"$@\"\n") f.close() os.chmod(o_name, stat.S_IRWXU) sys.exit(proc.returncode)
デザインに凝り過ぎた名刺は名刺スキャナ通らなかった
Evernoteでは名刺をスキャンする機能(名刺を写真で取ると取り込む)があります。 チョットデザインに凝ったものは失敗することが多いのでポイントをリストアップしました。
- コントラストは高め。
- ゴシック、明朝は読み取り精度に影響が少ないようだが、手書き風フォントは精度が落ちる(ただし英語名刺の場合は奇抜でなければ読み取り精度が高い)。
- 文字サイズは、よほど小さい場合以外は問題ない。
- 片面だけで名前、所属、連絡先の基本情報を全て記載する。
- 会社理念などを基本情報と同じ面にデザインすると認識精度が低くなる。
- 紙は矩形。角丸や独創的な形の名刺は台形補正が失敗する。
- 同様の理由で高コントラストの非矩形枠線も失敗要因になる。
- 横書き推奨。
- 他国産アルゴリズムでは縦書きの認識精度が低い。
- 斜めの場合も認識精度が低い。
- 1カラム構成が望ましい。2カラムの場所は左右でフォントサイズを変更する(役職と名前とか)。本社支社で2カラム構成はNG。
気づいたことがあったら加筆します。
PIAXで作る P2Pネットワーク
CMU #33で「PIAXで作るP2Pネットワーク」という題目で発表してきました。
メモ
- やっぱJavaScriptで使えればな〜という雰囲気があった。
PROCESS WARPが総務省異能vationプログラムの最終選考を通過しました。
ご助力、応援いただいている方々に御礼を申し上げるとともに、自分が何をするのか、したいのか、どうするかを見つめなおす機会でもあるなと思いました。