Anaconda の Numba を CUDA 9.0 で使う

Volta 対応の CUDA 9.0 が出ましたが、Anaconda の cudatoolkit は 8.0 までしか対応していないため、そのままでは Python  の Numba パッケージが使えません。

というわけで、悪戦苦闘した結果を書いておきます。

環境変数の設定

Numba を利用するためには以下の環境変数を設定する必要があります。

  • NUMBAPRO_NVVM
  • NUMBAPRO_LIBDEVICE

CUDA 9.0 をインストールすると /usr/local/cuda/nvvm/lib64/libnvvm.so.* というライブラリがが作成されますので、このパスを NUMBAPRO_NVVM に設定します。(ファイル名まで含む。)

また、/usr/local/cuda/nvvm/libdevice の下に libdevice.10.bc というファイルがありますので、シンボリックリンクで
libdevice.compute_XX.10.bc というリンクを貼っておきます。(XX は使用している GPU の Compute Capability を指定します。例えば GTX 1080 なら compute_61 となります。)NUMBAPRO_LIBDEVICE には /usr/local/cuda/nvvm/libdevice を指定します。

compute_XX の追加

Anaconda 5.0.0 の Numba には Compute Capability が 5.0 までしか登録されていないため、GPU によっては実行エラーが出てしまいます。ちょっと汚いやり方ですが、Numba パッケージに新しい Compute Capability を追加する方法で回避しました。

GTX 1080 用なら、具体的には ~/anaconda3/lib/python3.6/site-packages/numba/cuda/cudadrv/nvvm.py に以下の2点を追加します。

変数やクラス 修正内容
SUPPORTED_CC リストの最後に (6, 1) を追加
LibDevice クラスの _known_arch 変数 “compute_61” をリストの最後に追加

nVidia のデモプログラムへの修正

NVidia のサイトで説明しているサンプルは numbapro を使ったものなので、numba 用に修正が必要となります。

import numpy as np
from numpy.random import rand
from numba import vectorize

@vectorize(['float32(float32, float32)'], target='cuda')
def VectorAdd(a, b):
    return a + b

def main():
    N = 100000000 # 100M
    print("wait for initialization...")
    A = rand(N).astype(np.float32)
    B = rand(N).astype(np.float32)
    C = np.zeros(N, dtype=np.float32)
    print("OK. Let's start!")

    C = VectorAdd(A, B)
    print("C[:5] = " + str(C[:5]))

if __name__ == "__main__":
    main()

ちなみに、Anaconda3 は Intel の MKL を抱えているっぽいので、この程度の演算だと GPU の方が CPU より遅いという結果となりました。PCIe 上のメモリ転送がボトルネックを引き起こしているためと思われます。

デバイスメモリに結果をキャッシュするような例なら速くなるはず。

unordered_map から unordered_set は作れないのか?

連想配列を使っていると、キーの集合だけ、値の集合だけを作成したいことがある。そんな時はどうするのがスマートなのだろうか。

1.for 文で地道に拾う

C++ っぽくないので却下。

2.for_each 式+ラムダ式で拾う

using namespace std;

void separate(unordered_set<MapKey> key_set, unordered_key<MapValue> value_set, const unordered_map<MapKey, MapValue> map) {
  for_each(begin(map), end(map), [&key_set, &key_value] (const pair<MapKey, MapValue> &i) { key_set.insert(i->first), value_set.insert(i->second) });
}

Java と違って標準の抜き出しメソッドを用意していないのは set とか他のクラスに実装する可能性を考えているからだろうか。

どっちが正解??

スクリプト処理系で配列変数を定義するにあたり、どちらの実装にするべきか悩んでいる。

  1. 演算子 “[” を定義し、第1引数にベース、第2引数にキーを設定する。
  2. 変数に添え字配列を用意する。

1.の利点は固定数の引数に対して演算子を定義すればよいことで、実装が簡単になる。その分、実行エンジンは遅くなりますが。

2.はイメージとしては

class Content {
  Content *c;
  vector <Content *> pc;
};

として、pc が empty でない場合は連想配列の引数とみなすようにする。

2.の利点は実装が多少煩雑になるが、多重配列を定義する場合の実行速度を上げることができる。(ツリーをたどらず、O(1) の配列要素が利用できるので。)

最初1、の実装で考えていたが、途中で考え直して2.の実装に切り替えた。(コード上はその残骸が残っていますが)ただし、まだ変数を左辺値に指定すると NullException が発生するというバグがある。

久々にスクリプトエンジンのコードを書く

半年ぶりにスクリプトの実行エンジンのコードを書いた。

lex, yacc に相当するところは既に書き終えており、式の評価以外の実行エンジンも書いてあるので、残るは数式の評価である。

しかし半年もたってしまうとどういう構造だったかとか、このメンバーが何を意図していたかとかが分からなくなってしまい、困った。

何とか +, 変数代入、ハッシュが書けたので、後は淡々と定義した演算子の実体を定義することになる。この作業、本当に地道で根気のいる作業なのです。

PC 見積もり代金 530万円也

hp のサイトで、以下のスペックで PC の見積もりを見てみた。

  • CPU: Xeon 2620v4 2.1G 8コア×2
  • RAM: 512GB
  • HDD: 2TB ×2
  • GPU: P6000 ×2
  • その他もろもろ(Blue-ray, GbE追加など)

結果は標題にある通り、530万円余りということになった。
(CPU には正直あまり期待していないので、必要最小限のオプションになっています。)

ちなみにさくらインターネットの高火力シリーズでP100×1を1年間の見積もりを計算すると363万円(ただしメモリ1TB)、マウスコンピュータのX299ベース、128GBのPCを4台見積もると440万円くらいになるので、電源事情を考えれば、2年以上使うのだったら hp から購入するのは悪くない選択肢に思える。
(Ubuntu のホームページを見ると Z840 筐体はサポートされているようだ)

納期が8週間かかりそうなのと、11月下旬に新しい筐体が出るようなので、今回は購入を見送ります。というか今使っている PC を取りあえずフルに使えと言うことなんですが。

N-gram 検索エンジンに and 機能を追加

N-gram 検索エンジンに and 検索機能を追加しました。

半角の空白文字を入力すると、空白で区切られた文字列を全て含むページを検索します。

実装がちょっとうまくないですが、おいおい直します。

表示についても統一化していないため、検索にヒットした文の数だけ結果が表示されます。例えば、「可逆 圧縮」と入力した場合、「可逆」で検索された後、「圧縮」で検索されるため、「可逆」で1行、「圧縮」で1行表示されます。

これは検索の問題というよりは表示の問題です。

N-gram 解説記事を展開しました

GitHub にはすでにサンプルを上げてありますが、N-gram 解説記事を新たに展開しました。

使い古されてあまり顧みられなくなった感のある N-gram ですが、実装が簡単な割には厳密な検索が可能なので、個人的には重宝しています。

解説記事はネットに多数掲載されていますが、他の検索エンジン技術と比較するときに対象記事があったほうが便利なので書いてみました。

振り返ってみると、あまり万人向けの解説になっていませんね..。

GitHub に検索エンジンソースを追加

GitHub.com に検索エンジンのソースコードを追加しました。

まだ N-gram ベースのプログラムしかありませんが、おいおい他のテクノロジーベースのエンジンも収録していきたいと思っています。

詳細はそのうちホームページに記述したいと思いますが、プログラムは以下のような構成になっています。

  • scayping.py: トップページを指定するとその下にある全てのページを取得し、検索用の3つのファイルを作ります。(ただし、ブログは収録しないようになっています。)
  • search.php: 検索エンジンのラッパーで、UI を与えるためのプログラムです。
  • search_engine.php: 検索エンジンの本体で、bi-gram による検索を実行します。

なお、前の記事にも書いたとおり、まだ And/Or 検索条件が書けません。

検索エンジン追加

N-gram ベースの検索エンジンが動くようになりました。結局、索引ファイルの作成は python3 で、検索エンジンは php で組んでみました。

索引ファイルはここでは作成できないので、別のマシンで随時作成したものをアップロードします(ちょっとださい)。

なお、検索対象はホームページ側の記事のみで、ブログ記事は検索されません。ブログを検索したい場合はブログの中で検索をかけてください。

And/Or 検索も実装されていませんが、そのうち実装したいと思います。

しかし、たったこれだけ実装するのに1日かかるとは、コーディングの実力が落ちたもんだ。。。

検索エンジン(2)

今更クローラを C で組みたくないな、と思い始めた。

XML と違って HTML は独特の書き方をするサイトもあるので、結構クローリングは大変なのである。N-gram を収容するファイルのフォーマットは大体決まったので、python か何かのクローリングライブラリを使おうかと思う。

N-gram ファイルの概要

明日は仕事が休みらしいので、ヒマがあれば片付けてしまおう。