ABCLをちょっと触る

Scalaから何らかのLisp方言に乗り換える場合にはJVMのライブラリ関連があるからClojureを使うのが普通の流れな気がするが、Common LispにもJVMで動くABCLという処理系があるので試してみた。

Armed Bear Common Lisp (ABCL) - Common Lisp on the JVM

Docker使うととりあえず動く。

docker run -it easye/abcl

REPLが立ち上がるのでQuicklispを入れる。

(load "http://beta.quicklisp.org/quicklisp.lisp")
(quicklisp-quickstart:install)

GitHub - fukamachi/clack: Web server abstraction layer for Common Lisp

とりあえずhunchentootとclackを入れて、Usageにあるサンプルを動かしてみる。

(ql:quickload '(:hunchentoot :clack))
(defvar *handler*
    (clack:clackup
      (lambda (env)
        (declare (ignore env))
        '(200 (:content-type "text/plain") ("Hello, ABCL!")))))

UsageにはWebブラウザhttp://localhost:5000/ にアクセスしろ、とあるがdocker内で動作しているからdexadorで試す。

(ql:quickload :dexador)
(dex:get "http://localhost:5000/")

動いた。

"Hello, ABCL!"
200
#<EQUAL HASH-TABLE 4 entries, 11 buckets {33B1D2E7}>
#<QURI.URI.HTTP:URI-HTTP http://localhost:5000/>
#<TWO-WAY-STREAM {1A6F263C}>

HelixキーボードのhexファイルをUbuntuのavrdudeで書き込む

Helix キーボードキット | 遊舎工房 のドキュメントにはLinuxでの書き込み方が書いておらず、Releases · qmk/qmk_toolbox · GitHubLinux版も動かなかったので、コマンドラインから実行したメモ

# USBを接続する
stty -F /dev/ttyACM0 ospeed 1200 ispeed 1200
stty -F /dev/ttyACM0 speed 115200
# Helixのリセットボタンを押す
avrdude -p atmega32u4 -c avr109 -P /dev/ttyACM0 -U flash:w:helix_rev2_default.hex 

Nextremer退職

2018年6月で株式会社Nextremerを退職しました。理由はお察し下さい。

1年半チョットの在籍中、LEGOを動かしたり、ホビーロボを組み合わせて動かしたり ( ディープラーニングで「うまい棒」と「都こんぶ」を仕分けるマシン 20万回の訓練でつかむ位置まで正確に学習 - ねとらぼ)、ロボットシミュレーションの為にxml書いたりしていた。 後、機械学習Pythonも少し書いてた。

結果的に、遥か昔に辞めたはずのロボット開発に戻ってきたのは不思議な感じだった。何でもやっておくと役に立つもんだなあ。 そんなに深くやったわけではないけど、20年位前のロボット開発と比べて思うのは、色々パーツとかキットが抱負になったのと、3Dプリンターが安価になって使われてることか。

次の会社では再び職業Common Lisperになる予定 (2年ぶり2回目)

Graph 500のコードを動かしてみた

Graph 500 | large-scale benchmarks

Graph 500というスパコンベンチがあって、これのコードは公開されているので触ってみた。 スパコン持ってないので、実験機材は普通のノートPCにUbuntu 16.04が入ったもの。

まずはv3.0.0のコードをもらってくる。 https://github.com/graph500/graph500/archive/graph500-3.0.0.tar.gz

mpi関連をインストールする。

apt install mpich

展開する。

tar xzf graph500-3.0.0.tar.gz
cd graph500-graph500-3.0.0/

このままビルドするとリンクでエラーになるので、Makefile内のコンパイル引数に指定されている -lpthread とか -lm を後ろに持っていく。

ビルドする。

make

ビルド結果としては4つの実行ファイルが出来上がるが、名前に reference と付いている方は実行可能で、 custom と付いている方は動かない(本来の目的である、自分の実装をするためのものなので)。

ローカルでmpiの4並列実行してみる。

time mpiexec -n 4 ./graph500_reference_bfs 20

結果は長いので最後だけ抜粋。

SCALE:                          20
edgefactor:                     16
NBFS:                           64
graph_generation:               4.61012
num_mpi_processes:              4
construction_time:              1.36205
bfs  min_time:                  0.277266
bfs  firstquartile_time:        0.283024
bfs  median_time:               0.291698
bfs  thirdquartile_time:        0.302793
bfs  max_time:                  0.349763
bfs  mean_time:                 0.295806
bfs  stddev_time:               0.0167295
min_nedge:                      16775818
firstquartile_nedge:            16775818
median_nedge:                   16775818
thirdquartile_nedge:            16775818
max_nedge:                      16775818
mean_nedge:                     16775818
stddev_nedge:                   0
bfs  min_TEPS:                  4.79634e+07
bfs  firstquartile_TEPS:        5.54037e+07
bfs  median_TEPS:               5.75108e+07
bfs  thirdquartile_TEPS:        5.92735e+07
bfs  max_TEPS:                  6.05044e+07
bfs  harmonic_mean_TEPS:     !  5.67123e+07
bfs  harmonic_stddev_TEPS:      404095
bfs  min_validate:              1.18584
bfs  firstquartile_validate:    1.21473
bfs  median_validate:           1.22582
bfs  thirdquartile_validate:    1.26528
bfs  max_validate:              1.44657
bfs  mean_validate:             1.24752
bfs  stddev_validate:           0.0512569

real    1m50.265s
user    6m41.675s
sys 0m25.753s

なんとなく動いた。

Complete Results | Graph 500 を見るとパソコンレベルの1ノード構成でも1GTEPSくらいは出るみたいなので、max 0.06GTEPSはかなり遅い気がする。 reference だとそんなもんなんだろうか?

Common Lispで最速のfizzbuzzを実装した話

Kazuho氏のblogにこういうのがあったのでCommon Lispでやってみた。

blog.kazuhooku.com

Common Lispで実装する。

(defmacro fizzbuzz (n)
  (format nil "~{~a ~}" (loop for i from 1 below n
                              collect (cond
                                        ((= 0 (mod i 15)) "fizzbuzz")
                                        ((= 0 (mod i 5)) "buzz")
                                        ((= 0 (mod i 3)) "fizz")
                                        (t i)))))

(defun main ()
  (print (fizzbuzz 100)))

SBCL 1.4.1でディスアセンブルする。

* (disassemble #'main)

; disassembly for MAIN
; Size: 33 bytes. Origin: #x100195397C
; 7C:       498B4C2460       MOV RCX, [R12+96]                ; no-arg-parsing entry point
                                                              ; thread.binding-stack-pointer
; 81:       48894DF8         MOV [RBP-8], RCX
; 85:       488B15A4FFFFFF   MOV RDX, [RIP-92]                ; "1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz 16 17 fizz 19 buzz fizz 22 23 fizz buzz 26 fizz 28 29 fizzbuzz 31 32 fizz 34 buzz fizz 37 38 fizz buzz 41 fizz 43 44 fizzbuzz 46 47 fizz 49 buzz fizz 52 53 fizz buzz 56 fizz 58 59 fizzbuzz 61 62 fizz 64 buzz fizz 67 68 fizz buzz 71 fizz 73 74 fizzbuzz 76 77 fizz 79 buzz fizz 82 83 fizz buzz 86 fizz 88 89 fizzbuzz 91 92 fizz 94 buzz fizz 97 98 fizz "
; 8C:       B902000000       MOV ECX, 2
; 91:       FF7508           PUSH QWORD PTR [RBP+8]
; 94:       B8D8213620       MOV EAX, #x203621D8              ; #<FDEFN PRINT>
; 99:       FFE0             JMP RAX
; 9B:       CC10             BREAK 16                         ; Invalid argument count trap
NIL

文字列をprintするだけのmain関数が生成された。

Pythonのmultiprocessingおよびpipe実行時間

Pythonのmultiprocessingおよびpipeの実行時間計測。

multiprocessing

code

#!/usr/bin/env python
#
# see also : http://docs.python.jp/2.7/library/multiprocessing.html

from sys import stdout
from time import time
from multiprocessing import Process, Pipe

NUMBER = 1000

def measurement(f):
    time_start = time()
    for n in range(NUMBER):
        f()
    time_end = time()
    return time_end - time_start

def dummy():
    pass

def create_process():
    Process()

def start_process():
    Process().start()

def create_pipe():
    Pipe()

def join_process():
    process = Process()
    process.start()
    process.join()

def null_process(pipe1, number):
    for n in range(number):
        pipe1.recv()

def measurement_simplex_pipe():
    time_start = time()
    pipe0, pipe1 = Pipe()
    process = Process(target=null_process, args=(pipe1, NUMBER))
    process.start()
    for n in range(NUMBER):
        pipe0.send(n)
    process.join()
    time_end = time()
    return time_end - time_start

def echo_process(pipe1, number):
    for n in range(number):
        pipe1.send(pipe1.recv())

def measurement_duplex_pipe():
    time_start = time()
    pipe0, pipe1 = Pipe()
    process = Process(target=echo_process, args=(pipe1, NUMBER))
    process.start()
    for n in range(NUMBER):
        pipe0.send(n)
        pipe0.recv()
    process.join()
    time_end = time()
    return time_end - time_start

stdout.write("pass, %f\n" % measurement(dummy))
stdout.write("create process, %f\n" % measurement(create_process))
stdout.write("start process, %f\n" % measurement(start_process))
stdout.write("join process, %f\n" % measurement(join_process))
stdout.write("create pipe, %f\n" % measurement(create_pipe))
stdout.write("simplex pipe, %f\n" % measurement_simplex_pipe())
stdout.write("duplex pipe, %f\n" % measurement_duplex_pipe())

Python 2.7

処理 実行時間 (ms)
pass 0.000079
create process 0.003357
start process 0.245117
join process 0.976598
create pipe 0.011329
simplex pipe 0.003099
duplex pipe 0.011246

Python 3.5

処理 実行時間 (ms)
pass 0.000067
create process 0.003868
start process 0.415079
join process 1.492507
create pipe 0.035219
simplex pipe 0.007089
duplex pipe 0.047015

pipe

code

#!/usr/bin/env python
#
# see also : http://docs.python.jp/2.7/library/multiprocessing.html
#            http://docs.chainer.org/en/stable/tutorial/basic.html

from sys import stdout
from time import time
from multiprocessing import Process, Pipe
from chainer import Chain, ChainList
from chainer.links import Linear

NUMBER = 1000

def measurement(f):
    time_start = time()
    for n in range(NUMBER):
        f()
    time_end = time()
    return time_end - time_start

def null_process(pipe1, number):
    for n in range(number):
        pipe1.recv()

def measurement_simplex_pipe(klass):
    time_start = time()
    pipe0, pipe1 = Pipe()
    process = Process(target=null_process, args=(pipe1, NUMBER))
    process.start()
    for n in range(NUMBER):
        pipe0.send(klass)
    process.join()
    time_end = time()
    return time_end - time_start

def echo_process(pipe1, number):
    for n in range(number):
        pipe1.send(pipe1.recv())

def measurement_duplex_pipe(klass):
    time_start = time()
    pipe0, pipe1 = Pipe()
    process = Process(target=echo_process, args=(pipe1, NUMBER))
    process.start()
    for n in range(NUMBER):
        pipe0.send(klass)
        pipe0.recv()
    process.join()
    time_end = time()
    return time_end - time_start

class MyChain(Chain):
    def __init__(self):
        super(MyChain, self).__init__(
            l1=Linear(784, 100),
            l2=Linear(100, 100),
            l3=Linear(100, 10)
        )

    def __call__(self, x):
        h1 = relu(self.l1(x))
        h2 = relu(self.l2(h1))
        return self.l3(h2)

class MyChainList(ChainList):
    def __init__(self):
        super(MyChainList, self).__init__(
            Linear(784, 100),
            Linear(100, 100),
            Linear(100, 10)
        )

    def __call__(self, x):
        h1 = relu(self[0](x))
        h2 = relu(self[1](h1))
        return self.l3(h2)

classies = [Chain, ChainList, MyChain, MyChainList]

for k in classies:
    stdout.write("create instance %s, %f\n" % (k, measurement(lambda: k())))

for k in classies:
    stdout.write("simplex pipe %s, %f\n" % (k, measurement_simplex_pipe(k)))

for k in classies:
    stdout.write("duplex pipe %s, %f\n" % (k, measurement_duplex_pipe(k)))

Python 2.7

処理 実行時間 (ms)
create instance Chain 0.003006
create instance ChainList 0.001593
create instance MyChain 3.788916
create instance MyChainList 3.780761
simplex pipe Chain 0.005608
simplex pipe ChainList 0.005627
simplex pipe MyChain 0.006494
simplex pipe MyChainList 0.006871
duplex pipe Chain 0.029776
duplex pipe ChainList 0.034982
duplex pipe MyChain 0.033020
duplex pipe MyChainList 0.035376

Python 3.5

処理 実行時間 (ms)
create instance Chain 0.002134
create instance ChainList 0.001779
create instance MyChain 4.150180
create instance MyChainList 4.152344
simplex pipe Chain 0.013076
simplex pipe ChainList 0.013038
simplex pipe MyChain 0.013047
simplex pipe MyChainList 0.010454
duplex pipe Chain 0.065646
duplex pipe ChainList 0.070427
duplex pipe MyChain 0.076342
duplex pipe MyChainList 0.079283

ZenBook3のベンチマーク

SysBenchを使ってZenBook3のベンチマークを取ってみた。

インストール

apt install sysbench

ファイルIO

準備する。

cd /tmp
sysbench --num-threads=16 --test=fileio --file-total-size=3G --file-test-mode=rndrw prepare
sysbench 0.4.12:  multi-threaded system evaluation benchmark

128 files, 24576Kb each, 3072Mb total
Creating files for the test...

ランダム読み書き

helpにあったとおりにベンチマークを実行する。

sysbench --num-threads=16 --test=fileio --file-total-size=3G --file-test-mode=rndrw run
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 16

Extra file open flags: 0
128 files, 24Mb each
3Gb total file size
Block size 16Kb
Number of random requests for random IO: 10000
Read/Write ratio for combined random IO test: 1.50
Periodic FSYNC enabled, calling fsync() each 100 requests.
Calling fsync() at the end of test, Enabled.
Using synchronous I/O mode
Doing random r/w test
Threads started!
Done.

Operations performed:  6013 Read, 4009 Write, 12805 Other = 22827 Total
Read 93.953Mb  Written 62.641Mb  Total transferred 156.59Mb  (16.71Mb/sec)
 1069.46 Requests/sec executed

Test execution summary:
    total time:                          9.3711s
    total number of events:              10022
    total time taken by event execution: 0.0953
    per-request statistics:
         min:                                  0.00ms
         avg:                                  0.01ms
         max:                                  5.05ms
         approx.  95 percentile:               0.01ms

Threads fairness:
    events (avg/stddev):           626.3750/185.14
    execution time (avg/stddev):   0.0060/0.00

全然読み方がわからないけど、これ3GB指定してるけど3GBアクセスしてない?なんか読み書きのサイズ調整が必要なのか? とりあえずスレッド数を変更してたら数字が良かったのが8192。

sysbench --num-threads=8192 --test=fileio --file-total-size=3G --file-test-mode=rndrw run
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 8192

Extra file open flags: 0
128 files, 24Mb each
3Gb total file size
Block size 16Kb
Number of random requests for random IO: 10000
Read/Write ratio for combined random IO test: 1.50
Periodic FSYNC enabled, calling fsync() each 100 requests.
Calling fsync() at the end of test, Enabled.
Using synchronous I/O mode
Doing random r/w test
Threads started!
Done.

Operations performed:  4091 Read, 6009 Write, 11916 Other = 22016 Total
Read 63.922Mb  Written 93.891Mb  Total transferred 157.81Mb  (448.62Mb/sec)
28711.73 Requests/sec executed

Test execution summary:
    total time:                          0.3518s
    total number of events:              10100
    total time taken by event execution: 275.5066
    per-request statistics:
         min:                                  0.00ms
         avg:                                 27.28ms
         max:                                148.04ms
         approx.  95 percentile:             134.86ms

Threads fairness:
    events (avg/stddev):           1.2329/4.82
    execution time (avg/stddev):   0.0336/0.04

56MB/sくらい。 本当に8192スレッドで実行してるんだろうか。

シーケンシャル読み

sysbench --num-threads=16 --test=fileio --file-total-size=3G --file-test-mode=seqrd run
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 16

Extra file open flags: 0
128 files, 24Mb each
3Gb total file size
Block size 16Kb
Periodic FSYNC enabled, calling fsync() each 100 requests.
Calling fsync() at the end of test, Enabled.
Using synchronous I/O mode
Doing sequential read test
Threads started!
FATAL: Too large position discovered in request!
Done.

Operations performed:  196607 Read, 0 Write, 0 Other = 196607 Total
Read 3Gb  Written 0b  Total transferred 3Gb  (16.015Gb/sec)
1049527.74 Requests/sec executed

Test execution summary:
    total time:                          0.1873s
    total number of events:              196607
    total time taken by event execution: 2.5037
    per-request statistics:
         min:                                  0.00ms
         avg:                                  0.01ms
         max:                                 41.00ms
         approx.  95 percentile:               0.00ms

Threads fairness:
    events (avg/stddev):           12287.9375/3411.79
    execution time (avg/stddev):   0.1565/0.04

2GB/sくらい。 仕様上PCIe 3.0 x4接続でバスの片道理論性能が4GB/sだから、実効性能限界出てるかはわからないけどそれなりのオーダで計れてそうなのは分かった。

シーケンシャル書き

sysbench --num-threads=16 --test=fileio --file-total-size=3G --file-test-mode=seqwr run
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 16

Extra file open flags: 0
128 files, 24Mb each
3Gb total file size
Block size 16Kb
Periodic FSYNC enabled, calling fsync() each 100 requests.
Calling fsync() at the end of test, Enabled.
Using synchronous I/O mode
Doing sequential write (creation) test
Threads started!
Done.

Operations performed:  0 Read, 196608 Write, 128 Other = 196736 Total
Read 0b  Written 3Gb  Total transferred 3Gb  (581.51Mb/sec)
37216.69 Requests/sec executed

Test execution summary:
    total time:                          5.2828s
    total number of events:              196608
    total time taken by event execution: 25.3832
    per-request statistics:
         min:                                  0.00ms
         avg:                                  0.13ms
         max:                                 48.05ms
         approx.  95 percentile:               0.03ms

Threads fairness:
    events (avg/stddev):           12288.0000/1694.04
    execution time (avg/stddev):   1.5864/0.01

73MB/sくらい。

片付け

sysbench --num-threads=16 --test=fileio --file-total-size=3G --file-test-mode=rndrw cleanup

結論

これ真面目に色々やらないと正しい値が取れないな。。。