HPCメモ

HPC(High Performance Computing)に関連したりしなかったりすることのメモ書き

コンパイラ製品のコミュニティエディション

MSがVisual StudioのComunity editionをリリースしてはや3年(くらいだっけ?)が経ちますが、同じようなノリでIntelとPGIもCommunity editionを出してたようです。*1


MSのも合わせてとりあえずリンクを置いときます。
www.visualstudio.com

www.softek.co.jp

Qualify for Free Software | Intel® Software


3社とも通常の商用版からサポート等を無くしたもののようですが、それぞれ微妙に条件や動作環境が違っていて

MS
Windows版のみ、学生、個人、非営利団体、中小企業のみ利用可
PGI
Linux版/Mac版のみ(MAC版はGPGPU関連機能無し)、利用者の制限は無いがリリースから1年間のみ有効*2
Intel
Windows/Linux/Mac版のみ、学生、教員、OSS貢献者のみ利用可(ただしOSS貢献者はLinux版のみ)

となっています。

MSに関してはFortranコンパイラが無いのも一部の人には重要な違いかもしれません:p
あと、Intelに関してはコンパイラに付属しているいつものライブラリ(TBB/IPP/MKL)とData Analytics Acceralation Library*3は対象者の制限無しに公開されています。

software.intel.com

WindowsならVS community、LinuxMacならPGIコンパイラをインストールして、Intelのライブラリを呼び出すのが一番制約が少なそうですね。
PGIコンパイラに関しては並列デバッガ(PGDBG)*4も含まれてるようなので、これがうまく使えれば開発が楽になりますね。


ーーーー
2017/04/09 リンクのミスを修正し、Intelコンパイラのページへのリンクが見え難くなっていたのでPGIと順番を入れ変えました。

*1:去年の秋ごろには見かけたような気がするけど、twitterでつぶやいただけでblogでは放置してたみたいで・・・

*2:Sharp Zaurusの開発環境を思いだした

*3:DAALと略すのかな?

*4:使ったことないけど・・・

Macの初期設定&備忘録

私事ながら、昨年末で5年ちょっと勤めた某社を退職し、自分で会社を立ち上げました。
というわけで、「あーこのネタをブログに書きたいけど会社の人にバレたら困るなー」みたいな制約が無くなりましたんで、今年からはもっと記事を書く頻度を上げていきたいと思います。*1

最初の記事ですが、この機に長年使い慣れたWindowsを捨ててマカーになったので自分用の備忘録として、初期設定などでやったことをまとめておきます。

HHKの設定変更

dipスイッチの1,2をonにしないとコマンドキーが送出されないので、これを変更しておく。
この変更を有効にするとFn+Tabがcaps lockとして機能するようになるので注意!

キーボードの設定変更

[システム環境設定] -> [キーボード] -> [キーボード] と進んで"キーのリピート"と"リピート入力認識までの時間"のスライダーを一番右へ設定する。
f:id:n_so5:20170111105532p:plain

マウスの設定変更

[システム環境設定] -> [マウス] と進んで"スクロールの方向: ナチュラル"のチェックボックスを外し"軌跡の速さ"と"スクロールの速さ"を調整
f:id:n_so5:20170111105548p:plain

dockの設定

ドックにもともと登録されているアプリはごみ箱へdrag&dropすれば削除できる。
逆に新規にアプリを登録する時は[Finder] -> [アプリケーション]と進むと一覧が表示されるので、そこからdrag&drop

コピペ

copy/cut/pasteはWindowsだとControl+C/X/Vだけど、macではCommand+C/X/Vが割り当てられている。
修飾キーが違うだけなんで、システム環境設定のキーボードのところでControlとCommandを入れ替えるというのがメジャーな方法らしいですが、これをやると全てのショートカットキーに影響が出るので、始めて使う機能(最初に書いたスクリーンショットとか)をググって調べながら使う時に支障がでそうです。
でも、Control+C/X/Vを有効にするだけだったら、こちらの方法でOK
d.hatena.ne.jp


うちの場合シンプルにこの3項目だけを書いています。

$ cat ~/Library/KeyBindings/DefaultKeyBinding.dict 
{
  "^c" = "copy:";
  "^v" = "paste:";
  "^x" = "cut:";
}

ディレクトリ名はKeyBinding**s**なのにファイル名はKeyBinding.dictなのに気付かずに1時間くらいはまってたので、ご注意を・・・orz

あと、この方法だとfirefox上では表示されているHTMLに対しては無効だけど、アドレスバーやtext area内のドキュメントに対しては有効という謎の状態になるようです。
ま、command+C/X/Vも有効なままなのでControlで反応すればCommandを使えば良いだけの話ですが。

==== 追記 ====

skkのインストール&設定

skkAquaSKKをこちらからダウンロードしてインストールします。
github.com
このままだと、ターミナル上でC-jを入力してもかなモードに切り替わらないので、こちらのコメントにあるようにkeymapにC-7/C-8/C-9あたりを追加しておきます。*2
github.com

あとhome brew, macvim, vagrant, virtualboxあたりもインストールしてますが単純に入れただけなので省いてます。

*1:年明けにこういうことを書くblogってのは大抵更新されないもんだけど・・・

*2:C-jを入力して効かなかった時に、右手の人差し指を適当に上の方に動かせばOKというキーアサインです

python3でtrelloに登録したカードを読み出す(その2)

前回作ったプログラムですが、実行に10秒ほどかかっていて、これならブラウザで複数プロジェクトのボードを全部開いてまわってもたいして効率が変わらない感じです。
おそらく遅い原因はボード一覧の取得->ボード毎にリスト一覧の取得->リスト毎にカード一覧の取得という一連の流れの中で、毎回O(N)相当のhttpリクエストを投げてるところだろうと思います。なんとか早くする方法が無いかと思って調べてたんですが、trelloのAPIリファレンスのページを見てると、こういう複数リクエストをまとめて投げるAPIがありました。
https://developers.trello.com/advanced-reference/batch
これを使えばこのプログラムのhttpリクエストは次の3回までに減らすことができます。

  1. ボード一覧の取得
  2. ボード内のリスト一覧の取得
  3. 所定のリスト(WIPとかToDoとか)内のカード一覧の取得

さっそく、前回てきとーに作ったTrelloClientクラスにbatch_fetch関数を追加してみます。

class TrelloClient:
    def __init__(self, api_key, token):
        self.api_key=api_key
        self.token=token
        self.base_url="https://api.trello.com/1"
    def fetch(self, category, api, target_id, option=""):
        url='{}/{}/{}/{}?key={}&token={}{}'.format(self.base_url,category,target_id,api,self.api_key,self.token,option)
        with  urllib.request.urlopen(url) as r:
            return json.loads(r.read().decode('utf-8'))
    def batch_fetch(self, category, api, target_ids, option=""):
        batch_url=""
        for target_id in target_ids:
            batch_url += "/{}/{}/{}/{},".format(category, id, api,option)
        batch_url=urllib.parse.quote(batch_url.rstrip(','))
        url="{}/batch/?urls={}&key={}&token={}".format(self.base_url, batch_url, self.api_key, self.token)
        with  urllib.request.urlopen(url) as r:
            return json.loads(r.read().decode('utf-8'))

これを使って全ボード内のリストの一覧を取得するとこんな感じのコードになります。

tc=TrelloClient(trello_api_key, trello_token)
boards=tc.fetch(category="member", api="boards", target_id="me", option="&filter=open&fields=name")
board_ids=[board['id'] for board in boards]
lists=tc.batch_fetch(category="boards", api="lists", target_ids=board_ids, option="?filter=open&fields=name&fields=idBoard")

今の時点のコードはこんな感じになってます。
n_so5 / TaskReporter / commit / 7197ec9bb6da — Bitbucket


新旧バージョンで実行時間を比較してみると

elapsed time for first version = 1.22e+01 sec
elapsed time for second version = 1.32e+00 sec

10倍早くなってますね。

python3でtrelloに登録したtask(=カード)を読み出す

googleカレンダーのお気に入りの機能のひとつに、「毎日指定した時間にその日の予定をメールで送ってくる」というのがあります。
気分的には、オフィスのエレベーターを降りて自席まで歩いている間に、クリップボードを持った秘書さんが付いてきて今日の予定を読み上げてくって感じ*1なんですが、
残念ながら我々エンジニアはカレンダーに入っているスケジュール済タスクだけをチェックしていると、色々と破綻します。

私の場合は、カレンダーの他にプロジェクト毎の課題管理のためにtrelloを使っていて案件毎に個別のボードにカンバン方式でタスクを登録しています。
ところが、trelloでボードをまたいでカードを見ようとすると自分にアサイン済のカード一覧を見るしかないので、BacklogからTodoへ(もしくはWIPへ)カードを移動させては自分へアサインし、Doneリストへ移動させてはアサインを外すという面倒なことをやっていました。
しばらくはこの方式で頑張ってたんですが、割り込みタスクのカードを作ったりすると徐々に破綻していって、今となっては自分にアサインされたカードの一覧を見ても入っていないタスクの方が多い有様です。

というわけで、一念発起して自分の担当案件のうち、作業中のタスクとToDo項目をtrelloから取得して表示するプログラムを作ってみました。

基本方針

  • python3で作成
  • urllibで頑張る(requestは使わない)
  • 出力先はとりあえずコンソール

trelloのカードを取得する準備

trelloのカードを取得するためには、APIキーとTokenを用意する必要があります。
まずは、次のURLへアクセスします。
https://trello.com/app-key
一番上に「キー:」と書かれた下に文字列があるので、これをメモっておきます。
続いて、「Token:」と書かれたセクションに"Token"というリンクがあるので、ここをクリックするといつもの認証画面に遷移します。
f:id:n_so5:20160906212740p:plain
ここで許可とすると

You have granted access to your Trello information.

To complete the process, please give this token:

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

という形でトークンの文字列が表示されるので、最後の行の文字列をメモっておきます。
以降のスクリプトでは、鍵をtrello_api_keyという変数に、トークンをtrello_tokenという変数に保存している前提で書きます。

ボードの一覧を取得

ボードの一覧を取得するには、次のAPIを呼びます。
https://developers.trello.com/advanced-reference/member#get-1-members-idmember-or-username-boards

コードで書くとこんな感じになります。

import urllib.request
import json

boards=[]
with  urllib.request.urlopen("https://api.trello.com/1/members/me/boards?key={}&token={}&filter=open&fields=name".format(trello_api_key, trello_token)) as r:
    tmp=r.read().decode('utf-8')
    boards=json.loads(tmp)

optionとして渡している文字列のうち、filter=openはOpenなボードのみ取得することを、fields=nameはボードの名前とidのみを取得することを意味しています。
これで、boardsには各ボードのidと名前を格納した辞書のリストが入ります。

次に進む前に、trelloのAPIを叩いて返ってきたjsonpythonのオブジェクトに変換して返すところまでをクラスにしておきましょう。

class TrelloClient:
    def __init__(self, api_key, token):
        self.api_key=api_key
        self.token=token
        self.base_url="https://api.trello.com/1"
    def fetch(self, category, api, target_id, option=""):
        url='{}/{}/{}/{}?key={}&token={}{}'.format(self.base_url,category,target_id,api,self.api_key,self.token,option)
        with  urllib.request.urlopen(url) as r:
            return json.loads(r.read().decode('utf-8'))

tc=TrelloClient(trello_api_key, trello_token)
boards=tc.fetch(category="member", api="boards", target_id="me", option="&filter=open&fields=name")

Taskの取得

ここまででボードのID(と名前)の一覧が取得できたので、次は各ボードに対してこちらのAPIを使ってリストの一覧を取得します。
https://developers.trello.com/advanced-reference/board#get-1-boards-board-id-lists
この時オプション引数のcardsとcard_fieldsを使うと、各リストに含まれるカードの一覧も取得できるので、これを使ってまとめてボード内のカードを取得します。ただし、この方法で取得すると全てのリストに入っているカードが返ってきてるので、TodoかWIPのリストに入っているカードだけを後から抽出する必要があります。
コードはこんな感じになります。(前のコードの続きです。)

todo={}
wip={}
for board in boards:
    lists=tc.fetch(category="boards", target_id=board["id"], api="lists", option="&filter=open&fields=name&cards=open&card_fields=name")
    for list in lists:
        if len(list['cards']) > 0:
            if list["name"] in ("ToDo", "todo", "Todo", "To do", "to do"):
                    todo[board["name"]]=list['cards']
            if list["name"] in ("WIP", "W.I.P.", "W.I.P", "Doing", "doing"):
                    wip[board["name"]]=list['cards']

リスト名の表記ゆれを考慮して一応それっぽい名前のものは全部対象のリストとみなすようにしてあります。

出力

こんな感じで取得したTodoとWIPのカード名を順にprintしていきます。

print("現在作業中のタスク")
for project, tasks in wip.items():
    print(project,":")
    for task in tasks:
        print("  -",task['name'])
    print("")

print("Todo項目")
for project, tasks in todo.items():
    print(project,":")
    for task in tasks:
        print("  -",task['name'])
    print("")

おまけ

このコードでは、ボード内の全カードを取得してから、不要な(対象外のリストに入っている)カードの除いていますが、trello APIで同時に取得できるアイテムの上限は1000なので1000以上のカードが入っているボードがあると全部のtaskを拾うことができません。こういう状況が起き得る場合は、ボード内のリストを取得する時に以下のように一旦リスト名の一覧を取得してから、必要なリストのカードを改めて取得する必要があります。

for board in boards:
    lists=tc.fetch(category="boards", target_id=board["id"], api="lists", option="&filter=open&fields=name)
    for list in lists:
        cards=tc.fetch(category="lists", target_id=list["id"], api="cards", option="&filter=open&fields=name")

私の環境だと、こっちの方がだいたい倍くらい遅かったんですが、もともと6~7秒で終わっていたのが10秒程度に伸びるくらいの感覚なので今のところこっちを使っています。

*1:洋画とかでよくやってるイメージ

pythonのwebbrowserモジュール

2年ほど前に、「へび年だからpythonの勉強でもするか」とかいう冗談みたいな理由でpythonを使い始めたんですが、今日初めてこんな素敵なモジュールがあることを知りました。

21.1. webbrowser — 便利なウェブブラウザコントローラー — Python 3.5.2 ドキュメント

このモジュール、名前が示すとおりの挙動で、URLを受け取ると標準のブラウザで開くというものです。

うちの環境だと、コマンドプロンプトから

> python -m webbrowser -t "http://www.python.org"

とやると、firefoxが立ち上がって(あるいは、新しいタブを開いて)python.orgのトップページを表示してくれます。







じゃ、cygwinでやるとどうなるかというと
f:id:n_so5:20160711163329p:plain
とりあえず、テキストブラウザが立ち上がりました。Bとか反応しないのでw3mじゃないのは確かだけど、このソフト何かなーと思いながら、qを押すと
f:id:n_so5:20160711164003p:plain
linksだったようです。
Links - Wikipedia


これは、cygwinのデフォルトブラウザがlinksになっているというわけじゃなくて、単にwebbrowserモジュールが持っているブラウザリストの中から、探していって見つかったものを呼んでるだけでしょうかね?


しかし、これでスクリプトからブラウザを開かせたりできるので大変便利そうです。
#アプリ連携の認証とか使える・・・?

MPIライブラリ毎のプロセスマッピング指定方法

さっき、ひさびさにIntelMPIを使ってて、プロセスマッピングの指定ってどうやるんだっけと小一時間悩んでしまったので、自分用メモとして残しときます。

IntelMPIの場合、プロセスマッピングを指定するには以下の環境変数を使います。

  • I_MPI_PIN
  • I_MPI_PIN_MODE
  • I_MPI_PIN_RESPECT_CPUSET
  • I_MPI_PIN_RESPECT_HCA
  • I_MPI_PIN_CELL
  • I_MPI_PIN_DOMAIN
  • I_MPI_PIN_ORDER
  • I_MPI_PIN_PROCESSOR_LIST

といってもI_MPI_PINからI_MPI_PIN_RESPECT_HCAまでは通常はデフォルト値のままで良いので、指定するのは後半の4つのみです。

I_MPI_PIN_CELLに指定可能な値は"unit"か"core"のどちらかで、前者は論理コア後者は物理コア単位での割り当てを行うことを指定します。
HyperThreadingが有効な時に、なるべく1rankが使うコアを狭めたいけど同じ物理コアで2rank動かれるのは困る、なんて時にはcoreを指定すれば良さそうです。*1


I_MPI_PIN_DOMAINは1rankが使うコア数を指定するものです。
1ノード16コアの環境で、ノードあたり8プロセス実行とかだったら16/8で2と指定すれば良いのですが、autoに指定しておくとこれと同様な計算を自動的にやってくれます。また、OMP_NUM_THREADSの値と合わせるompというキーワードも使えます。
他にもsocket/core/nodeといった指定もできますし(それぞれ1ソケット/1コア/1ノードあたり1rankを割り当てる)cacheという値を指定すれば、cacheを共有している範囲をdomainとしてその範囲内には1プロセスしか割り当てません。
ただし -n オプションで指定されたプロセス数が、使うノードの範囲内に納まらなかった場合は1domain内に複数のrankが割り当てられることもあります。(その場合はrank0から順に各domainに1rankづつ割り当てて、余ったらまたdomain0から順に割り当ててといった感じになるはず)
それから、auto/ompもしくは明示的なdomainのサイズを指定した場合は続けて :layout という形でdomainのレイアウト方法を指定できます。
これは、compact/scatter/platformの3つの値が指定できて、compactだと同一domainがなるべく近いコアに、scatterだとなるべく遠いコアに割り当てられます。platformにすると単純にBIOSレベルでのcore id順(たぶん/proc/cpuinfoで見える順番)で割り当てられます。

まぁ、色々書いたけど ハイブリッドプログラムなら omp:compactでflat MPIのプログラムなら core にしておけばたいてい大丈夫でしょう。
ハイブリッドプログラムで非privateな領域にやたらとアクセスしてスラッシングが酷いとかなら、omp:scatterも有効かもしれませんが、そもそもそんなプログラムは書き換えるべk


I_MPI_PIN_ORDERはI_MPI_PIN_DOMAINで指定した各ドメインを実際に割り当てる時の順番を指定します。
指定できる値は、compact/scatter/spread/bunchの4つです。
reference manualに非常に分かりやすい解説があったので、詳細は省きますが、たぶんcompactしか使わない。


I_MPI_PIN_PROCESSOR_LISTは、DomainとOrderの組み合わせでは指定できないような面倒なパターンを指定する時に使えます。
例えばrank0から3は同一ソケット上の連続した3つの物理コアを使ってrank4は同じノードの別ソケットを使うとか。
これで細かく指定するのは、コアを余らせて実行するような時なので、普段はあんあり出番は無いです。(ベンチマークとる時はそこそこ使う)

最後に、この辺のプロセスマッピングの状況を出力させるには

export I_MPI_DEBUG=4

を指定しておけばOKです。(正確に4じゃなくても4以上の値ならOK)

*1:でも、そもそもHPC用途だと大抵HT切ってるから使ったこと無いw

Alces Flight

starclusterだのcfnclusterだのをいじっていたら、なんかまた新しいサービスが始まってたようです。

Alces Flight: Effortless HPC is Born (Finally)

AWS marketplaceから起動できるので、これはこれで便利そうですね。


こちらは、starclusterやcfnclusterと違ってアプリのインストール手段を提供しているというのが特長らしくこんな感じの長いソフトの一覧が用意されています。*1

Alces Gridware Software Applications — flight-appliance-docs 1.0 documentation


個人的には興味無しだけど、プログラムをいじらずにひたすら計算する人*2には良いサービスかもしれませんね。


しかし、なぜかスケジューラはOpen grid schedulerが入っているそうで・・・なんでみんなそんなにSGE系のスケジューラが好きなんだろうか。

*1:なんかカテゴリに偏りがある気が・・・

*2:まるで学生時代の私だ:p