I presented a native compiler for 8-bit PIC microcontroller written in Common Lisp at Lisp Meet Up #27.
You can see its source code here.
I presented a native compiler for 8-bit PIC microcontroller written in Common Lisp at Lisp Meet Up #27.
You can see its source code here.
Falling Rigid Bunnies
Bunny-shaped fluid simulation
Rigid-fluid coupling for SPH
Rigid-fluid coupling for SPH
Rigid body simulation on a slope
Particle-based rigid body simulation
Braking wave simulation with SPH
Particle-based fluid simulation in x2 scale
Particle-based fluid simulation in 3D
Particle-based fluid simulation(defpic init ()
(progn
(setreg :gpio #x0) ; clera GP0-5
(setreg :cmcon0 #x07) ; disable comparator
(setbank1) ; switch to bank 1
(setreg :trisio #x08) ; only GP3 is outputinputmode
(setreg :ansel #x00) ; disable analog IO
(setreg :ioc #x00) ; disable interruption
(setbank0) ; switch to bank 0
(setreg :intcon #x00))) ; disable interruption
(defpic main ()
(progn
(setreg :gpio #x20) ; set GP5 to high
(mdelay 50) ; delay for 50 msec
(setreg :gpio #x00) ; set GP5 to low
(mdelay 950) ; delay for 950 msec
(main))) ; repeat
(defpic mdelay1 ()
(loop 90 ; 90 is a magic number to delay
0)) ; for 1 msec
(defpicmacro mdelay (n)
(unless (<= 0 n 65535)
(error "The value ~S is invalid." n))
(multiple-value-bind (q r) (truncate n 256)
(if (= q 0)
`(loop ,r (mdelay1))
`(loop ,q (loop ,r (mdelay1))))))
Then pic-compile function compiles and outputs the complete assembly for the PIC functions to standard output. The output assembly is expected to be assembled with Microchip's MPASM assembler.PIC> (pic-compile)
INCLUDE"p12f683.inc"
list p=12f683
__CONFIG _CP_OFF & _CPD_OFF & _WDT_OFF & _BOD_ON & _IESO_OFF& _PWRTE_ON & _INTOSCIO & _MCLRE_OFF
CBLOCK 020h
L0,L1,L2,L3,L4,L5,L6,L7 ; local registers
I0,I1,I2,I3,I4,I5,I6,I7 ; input registers
SP,STMP,STK ; stack registers
ENDC
ORG 0
GOTO MAIN
...
END
; No value
For detail, please see its repository.Swank RPC プロトコルは、SLIME と Swank との間で用いられる、テキストベースの非同期通信プロトコルです。
SLIME は、Common Lisp でアプリケーションを開発するための Emacs モードで、IDE(統合開発環境)と呼べるほど強力な機能を備えています。
SLIME には、Swank と呼ばれるバックエンドがあります。Swank は、Common Lisp プロセスにロードされ、ソケットを経由して SLIME と通信します。このとき使用されるのが、Swank RPC プロトコルです。
Swank RPC プロトコルは、6文字の16進数文字列とそれに続くS式、終端の改行文字で構成されます。
000016(:return (:ok nil) 1)\n
先頭の16進数文字列は、続くS式の長さ(最後の改行文字を含む)を表します。S式は、SLIME や Swank によって読み込まれます。S式の最初の要素はメッセージの種類を表すキーワードで、残りの要素がその引数です。
メッセージには、以下のような種類があります。
たとえば、(+ 3 4)というS式を Swank で評価するには、以下のメッセージを送信します。
00002c(:emacs-rex (+ 3 4) "COMMON-LISP-USER" t 1)\n
最初の6文字は続くS式の長さで、改行文字を含め44文字のため、16進数で2cとなります。
メッセージの種類は :emacs-rex (Remote EXecute SEXP)で、第一引数が評価するS式、第二引数がそのときのカレントパッケージです。第三引数はS式の評価を行う Swank 側のスレッドを表し、第四引数は戻り値を受け取る継続を意味します。
S式が評価されると、Swank からは以下のようなメッセージが返信されます。
000013(:return (:ok 7) 1)
最初の6文字は、送信されたメッセージと同様にS式の長さを表し、19文字のため、16進数で13となります。
戻り値を表すメッセージは、:return です。評価に成功した場合、第一引数の最初の要素が :ok となり、二番目の要素に評価した結果の値が入ります。ここではその値は7です。第二引数は、送信されたメッセージの第四引数に含まれていた継続がそのまま返されます。
このように、Swank RPC プロトコルでは、S式で表現されたテキストベースのメッセージが相互にやりとりされます。SLIME と Swank のやり取りの様子は、Emacs の *slime-events* バッファでみることができます。
;; define a coroutine using DEFCOROUTINE macro
(defcoroutine example (whom)
(format t "First greeting to: ~A~%" whom)
(yield 1)
(format t "Second greeting to: ~A~%" whom)
(yield 2)
(format t "Third greeting to: ~A~%" whom)
(coexit 3)
(format t "No greeting to: ~A~%" whom)
(yield 4))
=> EXAMPLE
;; make a coroutine object
(setf coroutine (make-coroutine 'example))
=> a coroutine object
;; funcall it
(funcall coroutine "Smith")
>> First greeting to: Smith
=> 1
;; SLIME open-directory short cut
(defun slime-open-directory (directory)
"open DIRECTORY with Finder"
(interactive (list (read-directory-name "Directory: " nil nil t)))
(let ((dir (expand-file-name directory)))
(call-process "open" nil nil nil dir)
(message "opened: %s" dir)))
(defslime-repl-shortcut nil ("open-directory" "open")
(:handler 'slime-open-directory)
(:one-liner "Open a directory with Finder"))"5. Modules" in The Haskell 98 Report ... Modules may be mutually recursive. ...ところが、GHCでHaskellのコードをコンパイルする場合、そのままではモジュールを相互参照させることができずエラーとなります。
- Examples/
- SimulationDSL/
- SimulationDSL.hs
- SimulationDSL/
- Compiler/
- Data/
- Interpreter/
- Language/
- Test/
$ cd Examples $ ghc -i../SimulationDSL --make -O2 CompilerRun.hs $ ./CompilerRun
$ time ./CompilerRun > /dev/null real 0m0.949s user 0m0.927s sys 0m0.011s $ time ./NBody > /dev/null real 0m0.972s user 0m0.950s sys 0m0.011s $ time ./InterpreterRun > /dev/null real 0m2.932s user 0m2.906s sys 0m0.022s
- Examples/ 実行例をまとめています
- SimulationDSL/
- SimulationDSL.hs モジュール外へのエクスポート
- SimulationDSL/
- Compiler/ コンパイラに関するコード
- Data/ 基本的なデータ構造
- Interpreter/ インタプリタに関するコード
- Language/ シミュレーション記述言語の構文と解釈
- Test/ テストを追加していく予定
$ cd Examples $ ghc -i../SimulationDSL --make -O2 CompilerRun.hs $ ./CompilerRun
$ time ./CompilerRun > /dev/null real 0m0.949s user 0m0.927s sys 0m0.011s $ time ./NBody > /dev/null real 0m0.972s user 0m0.950s sys 0m0.011s $ time ./InterpreterRun > /dev/null real 0m2.932s user 0m2.906s sys 0m0.022s
【まとめ】CG、シミュレーション関係の論文 
nBody =
do define "x" (integral (var "v" <*> dt))
define "v" (integral (var "a" <*> dt))
defineWithType "a" (var "f" </> m) ExpTypeVector
define "f"
(let r = norm (var' "x" <-> var "x")
k = m <*> m <*> g </> r </> r
n = (var' "x" <-> var "x") </> r
in sigma (k <*> n))
initialConditionV "x" x0
where dt = constantS 0.01
m = constantS 1
g = constantS 9.8
x0 = V.fromList [ (0,0,0), (2,0,0), (0,2,0) ]
main = mapM_ (printRow . flip machineRegisterValueV "x")
$ take 100 $ runSimMachine nBody
* for the whole code, see Simulation.hs in GitHub$ time ./Simulation > /dev/null real 0m2.900s user 0m2.869s sys 0m0.020s $ time ./NBody > /dev/null real 0m0.846s user 0m0.829s sys 0m0.011s

nBody =
do define "x" (integral (var "v" <*> dt))
define "v" (integral (var "a" <*> dt))
defineWithType "a" (var "f" </> m) ExpTypeVector
define "f"
(let r = norm (var' "x" <-> var "x")
k = m <*> m <*> g </> r </> r
n = (var' "x" <-> var "x") </> r
in sigma (k <*> n))
initialConditionV "x" x0
where dt = constantS 0.01
m = constantS 1
g = constantS 9.8
x0 = V.fromList [ (0,0,0), (2,0,0), (0,2,0) ]
main = mapM_ (printRow . flip machineRegisterValueV "x")
$ take 100 $ runSimMachine nBody
※コード全体についてはこちらを見てください$ time ./SimLang > /dev/null real 0m2.900s user 0m2.869s sys 0m0.020s $ time ./NBody > /dev/null real 0m0.846s user 0m0.829s sys 0m0.011sHaskellで直接書いた場合に比べて、1/3〜1/4の速度になっています。
【まとめ】CG、シミュレーション関係の論文 #include <stdio.h>
#include <stdlib.h>
#define N 1000
int main ()
{
double A[N][N], B[N][N], C[N][N];
int i, j, k;
// initialize the matrices
for ( i = 0; i < N; i++ )
for ( j = 0; j < N; j++ )
{
A[i][j] = 1.0;
B[i][j] = 1.0;
C[i][j] = 0.0;
}
// matrix multiplication
for ( i = 0; i < N; i++ )
for ( j = 0; j < N; j++ )
for (k = 0; k < N; k++ )
C[i][j] += A[i][k] * B[k][j];
printf ( "%f\n", C[0][0] );
return 0;
}
$ cat code2.c
#include <stdio.h>
#include <stdlib.h>
#define N 1000
int main ()
{
double A[N][N], B[N][N], C[N][N];
int i, j, k;
// initialize the matrices
for ( i = 0; i < N; i++ )
for ( j = 0; j < N; j++ )
{
A[i][j] = 1.0;
B[i][j] = 1.0;
C[i][j] = 0.0;
}
// matrix multiplication
int ibl = 100;
int ib, jb, kb;
for (ib=0; ib<N; ib+=ibl)
for (jb=0; jb<N; jb+=ibl)
for (kb=0; kb<N; kb+=ibl)
for (i=ib; i<ib+ibl; i++)
for (j=jb; j<jb+ibl; j++)
for (k=kb; k<kb+ibl; k++)
C[i][j] += A[i][k] * B[k][j];
printf ( "%f\n", C[0][0] );
return 0;
}
$ gcc -Wl,-stack_size,1000000000 -O2 -o code1 code1.c $ time ./code1 > /dev/null real 0m7.595s user 0m7.494s sys 0m0.035s $ gcc -Wl,-stack_size,1000000000 -O2 -o code2 code2.c $ time ./code2 > /dev/null real 0m2.509s user 0m2.462s sys 0m0.030s* The -Wl option is to designate ld to allocate large arrays on the stack
#include <stdio.h>
#include <stdlib.h>
#define N 1000
int main ()
{
double A[N][N], B[N][N], C[N][N];
int i, j, k;
// initialize the matrices
for ( i = 0; i < N; i++ )
for ( j = 0; j < N; j++ )
{
A[i][j] = 1.0;
B[i][j] = 1.0;
C[i][j] = 0.0;
}
// matrix multiplication
for ( i = 0; i < N; i++ )
for ( j = 0; j < N; j++ )
for (k = 0; k < N; k++ )
C[i][j] += A[i][k] * B[k][j];
printf ( "%f\n", C[0][0] );
return 0;
}
$ cat code2.c
#include <stdio.h>
#include <stdlib.h>
#define N 1000
int main ()
{
double A[N][N], B[N][N], C[N][N];
int i, j, k;
// initialize the matrices
for ( i = 0; i < N; i++ )
for ( j = 0; j < N; j++ )
{
A[i][j] = 1.0;
B[i][j] = 1.0;
C[i][j] = 0.0;
}
// matrix multiplication
int ibl = 100;
int ib, jb, kb;
for (ib=0; ib<N; ib+=ibl)
for (jb=0; jb<N; jb+=ibl)
for (kb=0; kb<N; kb+=ibl)
for (i=ib; i<ib+ibl; i++)
for (j=jb; j<jb+ibl; j++)
for (k=kb; k<kb+ibl; k++)
C[i][j] += A[i][k] * B[k][j];
printf ( "%f\n", C[0][0] );
return 0;
}
$ gcc -Wl,-stack_size,1000000000 -O2 -o code1 code1.c $ time ./code1 > /dev/null real 0m7.595s user 0m7.494s sys 0m0.035s $ gcc -Wl,-stack_size,1000000000 -O2 -o code2 code2.c $ time ./code2 > /dev/null real 0m2.509s user 0m2.462s sys 0m0.030s※大きな配列をスタック上に確保するために、ldにオプションを渡してスタックサイズを大きくしています
100 : 2.311s 200 : 2.390s 250 : 2.509s 500 : 6.588s※ブロック化のサイズの単位は、配列の要素数です。バイト数に直すと、double型の配列なので8を掛けたものになります
sample - caller.lisp ;; callee.lispで定義されている関数を使う - callee.lisp ;; caller.lispから呼ばれる - sample.asd ;; asdfモジュール定義ファイル
$ cat caller.lisp
(in-package :common-lisp)
(defpackage sample.caller
(:use :common-lisp
:sample.callee))
(in-package :sample.caller)
(hello-world)
$ cat callee.lisp
(in-package :common-lisp)
(defpackage sample.callee
(:use :common-lisp)
(:export :hello-world))
(in-package :sample.callee)
(defun hello-world ()
(print "Hello, World!"))
$ cat sample.asd
(defsystem "sample"
:components ((:file "callee")
(:file "caller"))
:serial t)
(asdf:load-system 'sample)