Rustでレイトレーシング ① 画像出力

e-tipsmemo.hatenablog.com

何はともあれ画像を出力しないと何もできないので
image crateについて軽くメモ

f:id:katakanan:20171228212253p:plain:w300
左上が原点のようだ

このcargoとcrateの組み合わせで非常に簡単にライブラリの導入ができるのが頼もしい。

Rustでレイトレーシング 0 準備

レイトレーシングを常々やってみようと思っていていた。
ちょうどよくRustという言語が波に乗り始めているらしいので
ついでにRustでレイトレーシングを書く

とは言っても0から何も見ずに書くのは学習も進まないので

これを参考に
C++のアナロジーでRustを学習しつつレイトレーシングを書いてゆく。

本の方はステップアップ方式で書いてあるが完成形は既にアップロードされており
この本の筆者のgithubは以下である。
github.com

今回は準備編

$ cargo new rust_ray01 --bin
$ cargo run
Compiling rust_ray01 v0.1.0 ()
Finished dev [unoptimized + debuginfo] target(s) in 0.96 secs
Running `target/debug/rust_ray01`
Hello, world!

最初に利用するcrate

  • image
  • nalgebra

Cargo.tomlに以下を追記

[dependencies]
image = "*"
nalgebra = "*"

必要になったcrateは順次追加

ディレクトリ構成

ファイル分割を容易にするために、以下も追記

[lib]
name = "renderer"
path = "src/lib.rs"

./src/lib.rsをmain.rsから見たrenderer crateとして使う?

f:id:katakanan:20171226225712p:plain:w400
core以下に色々と置く予定

Hello world

main.rs

extern crate renderer;
use renderer::*;

fn main() {
    println!("Hello, world!");
    println!("{}", hello());
}

lib.rs

extern crate image;
extern crate nalgebra as ns;

pub fn hello() -> String {
    "hello".to_string()
}
$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.77 secs
     Running `target/debug/rust_ray01`
Hello, world!
hello

zynqで最新のUbuntuを起動する

Ubuntu 17.04が出ました。

SDカードのフォーマット

gpartedが便利。
f:id:katakanan:20170608204912p:plain

SDカードの先頭から開始セクタ2048のところから
64MBをFAT16
その次から全部をext4にフォーマットする。
ラベルは適当

Ubuntuのインストール

Ubuntu 17の名前はzesty
SDの第二パーティションをマウントして
ここと同じようにして
e-tipsmemo.hatenablog.com

$ sudo debootstrap --foreign --arch armhf zesty /mnt http://ports.ubuntu.com/
(略)

kernelのビルド

こっからもらってきて
github.com

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- xilinx_zynq_defconfig  
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- UIMAGE_LOADADDR=0x8000 uImage  

uImageができる。

u-bootのビルド

ここからもらってきて
github.com

sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- zynq_zybo_config
sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-

u-bootができる。

Zynqの設定

f:id:katakanan:20171224111820p:plain
ここから

  • BitStream

SDKを起動して

  • fsbl
  • devicetree

を作る
devicetreeは
github.com
を利用する。
e-tipsmemo.hatenablog.com
と同じだが、
devicetreeのbootargsに

console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4 rootwait devtmpfs.mount=1
を指定する。

起動

ZyboにSDを指して電源を入れる。
うまく行けばFSBLが起動してu-bootが起動してKernelに制御が移って、
第二パーティションUbuntuのルートファイルシステムを展開してくれる。

zybo@ubuntu:~$ cat /etc/os-release
NAME="Ubuntu"
VERSION="17.04 (Zesty Zapus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 17.04"
VERSION_ID="17.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=zesty
UBUNTU_CODENAME=zesty
zybo@ubuntu:~$
zybo@ubuntu:~$ cat /proc/cpuinfo
processor       : 0
model name      : ARMv7 Processor rev 0 (v7l)
BogoMIPS        : 650.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpd32
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x3
CPU part        : 0xc09
CPU revision    : 0

processor       : 1
model name      : ARMv7 Processor rev 0 (v7l)
BogoMIPS        : 650.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpd32
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x3
CPU part        : 0xc09
CPU revision    : 0

Hardware        : Xilinx Zynq Platform
Revision        : 0003
Serial          : 0000000000000000
zybo@ubuntu:~$

よさそう

Rust multithread 確認

e-tipsmemo.hatenablog.com
このような記事を書いたが、そもそもthreadをたくさんjoinした後に、
その後の処理はそれらスレッドの終了を待っているのだろうか。と思ったので確認してみた。

#[cfg(test)]
mod tests {
    use super::*;

    fn hoge(thread: u32, num: u32) -> u32 {
        for x in 0..num {
            println!("th:{}, count:{}", thread, x);
            thread::sleep(std::time::Duration::from_millis(1000));
        }
        thread
    }

    #[test]
    fn thread_barrer() {
        let v = vec![20, 30, 10, 15, 17];
        let mut children = vec![];
        for t in 0..v.len() {
            let num = v[t as usize];
            children.push(thread::spawn(move || hoge(t as u32, num)));
        }

        let mut ret = vec![];

        for child in children {
            ret.push(child.join().unwrap());
        }

        println!("{:?}", ret.iter().fold(0, |s, &t| s + t));
    }
}

結果としてこのプログラムは全てのchild threadがおわってから次の処理を行うという意図したものになってた。

$ cargo test
   Compiling multi v0.1.0 (*** )
    Finished dev [unoptimized + debuginfo] target(s) in 1.3 secs
     Running target/debug/deps/multi-1bf733f9a047a566

running 1 test
th:2, count:0
(略)
th:2, count:3
th:1, count:3
th:3, count:3
th:0, count:3
th:4, count:3
th:1, count:4
th:4, count:4
th:2, count:4
th:3, count:4
th:0, count:4
th:0, count:5
th:3, count:5
th:4, count:5
th:2, count:5
th:1, count:5
(略)
th:0, count:12
th:1, count:12
th:3, count:12
th:4, count:12
th:0, count:13
th:4, count:13
th:3, count:13
th:1, count:13
th:0, count:14
(略)
th:1, count:20
th:1, count:21
th:1, count:22
th:1, count:23
th:1, count:24
th:1, count:25
th:1, count:26
th:1, count:27
th:1, count:28
th:1, count:29
10
.
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

Correct way to wait until all threads are finished
結論:勝手に待ってくれている。

並行コンピューティング技法 ―実践マルチコア/マルチスレッドプログラミング

並行コンピューティング技法 ―実践マルチコア/マルチスレッドプログラミング

Mastering Rust: Advanced concurrency, macros, and safe database

Mastering Rust: Advanced concurrency, macros, and safe database

EZ-USB FX2 開発環境設定・動作テスト

CypreeのEZ-USB FX2を開発するために必要なものをメモする

まずは登録しないといけないのが不便
あとサイプレスのサイトがとても重い。

開発ソフトウェア

Windows10のためのドライバ
http://japan.cypress.com/knowledge-base-article/drivers-ez-usb-fx1-fx2lp-and-fx3-kba94413


開発ソフトウェアはFX2LP用のSDKがあるがKeilコンパイラを使用してIDEも使いにくい
http://japan.cypress.com/documentation/development-kitsboards/cy3684-ez-usb-fx2lp-development-kit

メモ

EZ-USB FX2には8051という8bit CPUが載っており
Cypress付属のコンパイラ のKail Compilerでは4KBまでのプログラムしかコンパイルすることができない。

EclipseでFX3のSDKを入れる。これには同時にFX2LPの開発もできるようになっている。
www.cypress.com

この中には
Cypress Console
http://japan.cypress.com/documentation/software-and-drivers/suiteusb-34-usb-development-tools-visual-studio
も既に入っている。
f:id:katakanan:20171231115031p:plain

コンパイラは付属していなので
Keilコンパイラの代わりにSDCCコンパイラを入れる。
SDCC - Small Device C Compiler
SDCCをProgram Files以下にインストールするとEclipseがProgramをコマンドだと思うようになるのでC:\SDCCにインストールした。

すべてをインストールする。

プロジェクト作成

すべてのプログラムのCypressの中に
Cypress EZ USB SuiteというEclipseベースのIDEがある。
f:id:katakanan:20171231115636p:plain

File->Project でCypessを展開すると、FX2LP Projectがある
f:id:katakanan:20171231115731p:plain

Bulkloopをテンプレートにプリジェクトを作成できる。
f:id:katakanan:20171231115815p:plain

コンパイル

EclipseでBuild All

書き込みテスト

バイスマネージャーにCypress FX2LP No EEPROM Deviceと出ればOK
f:id:katakanan:20171231152441p:plain

Cypress Control Centerを起動して、Cypress No Flash を選択する。
f:id:katakanan:20171231152554p:plain

ツールバーのProgram FX2からRAMに
f:id:katakanan:20171231152654p:plain
ビルドしたサンプルプログラムのディレクトリ内にあるbulklo]op.hexを選択し、書き込む。

f:id:katakanan:20171231153108p:plain
左下のProgramm....が Successに変われば書き込み完了である。

f:id:katakanan:20171231153125p:plain

注意

RAMに書き込んだので電源を落としてしまうとプログラムは消えてしまう。デバッグ中はRAMに書き込んで、実用するときにFlash ROMに書き込むという思想のようだ。

動作

プログラムを書き込んで数十秒立つと、Cypress Control Center上でのFX2 の表示が
Cypress FX2LP Sample Deviceに代わっている。
f:id:katakanan:20171231153235p:plain

要素を展開して、Endpointを指定し、転送を開始すると0を
Byte to transferのサイズだけ送信するテストが動く。
Text to send にテキストを入力するとそのバイトサイズだけ転送される。
f:id:katakanan:20171231153411p:plain

次からは以下の書籍を参考にしていこうと思う。(しかし絶版らしい)
[asin:4885549612:detail]

Rust multithread 実行時間測定

multithreadが本当に効いているのかを確かめたいので
シングルスレッドとマルチスレッドで実行時間を測定したかった。

しかし、
ここのような
e-tipsmemo.hatenablog.com
cargo banchによる測定方法ではなんだかうまくいかなかったので
シンプルに実行前後の時間の差分をとる補法で比較した。

e-tipsmemo.hatenablog.com

time crateを使うのでCargo.tomlに追記する。

[dependencies]
time = "*"

main.rs

extern crate time;

use std::u32;
use std::thread;
use std::sync::{Arc, Mutex};

use test::test::Bencher;
use time::precise_time_ns;

#[derive(Debug, Copy, Clone)]
struct Fx {
    a: f64,
    b: f64,
    step: u64,
    f: fn(f64) -> f64,
}

impl Fx {
    fn new(a: f64, b: f64, step: u64, f: fn(f64) -> f64) -> Fx {
        Fx {
            a: a,
            b: b,
            step: step,
            f: f,
        }
    }
}

fn integral(p: &Fx) -> f64 {
    let dx = (p.b - p.a) / (p.step as f64);

    let mut sum = 0.0;

    for n in 0..p.step {
        let x = p.a + dx * (n as f64);
        let bar = ((p.f)(x) + (p.f)(x + dx)) * dx / 2.0;
        sum = sum + bar;
    }
    sum
}

#[cfg(test)]
mod tests {
    use super::*;

    const a: f64 = 0.0;
    const b: f64 = 1.0;
    const thread: u64 = 4;
    const onestep: u64 = 100000000;

    fn f(x: f64) -> f64 {
        x * x + 1.0
    }

    #[test]
    fn int_test() {
        let prim = Fx::new(a, b, thread * onestep, f);
        let t0 = precise_time_ns();
        let ret = integral(&prim);
        let t1 = precise_time_ns();
        println!("single:{:?}", ret);
        println!("exec time:{}", t1 - t0);
    }

    #[test]
    fn int_thread_test() {
        let mut pvec = vec![];
        let x = (b - a) / (thread as f64);
        let ti = thread as usize;

        for t in 0..thread {
            let at = a + x * (t as f64);
            let bt = a + x * (t as f64) + x;
            let prim = Fx::new(at, bt, onestep, f);
            pvec.push(prim);
        }

        let mut children = vec![];
        let mut data = vec![];

        for i in 0..ti {
            let p = pvec[i];
            children.push(thread::spawn(move || integral(&p)));
        }

        let t0 = precise_time_ns();
        for child in children {
            data.push(child.join().unwrap());
        }

        let res = data.iter().fold(0.0, |s, &t| s + t);
        let t1 = precise_time_ns();

        println!("{:?}", data);
        println!("multi:{:?}", res);
        println!("exec time:{}", t1 - t0);
    }
}

fn main() {
    let handle = thread::spawn(|| {
        println!("Hello, thread World!");
    });

    let _ = handle.join();
}

実行結果

$ cargo test
running 2 tests
[0.255208333333325, 0.2864583333333229, 0.3489583333333, 0.44270833333330617]
multi:1.333333333333254
exec time:12128613000
.single:1.3333333333333182
exec time:30163525000
.
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured

速く。。なっている?気がする。
これは実行部分の時間しか測定しておらず、
ThreadHandleを取得するところも含めるとマルチスレッドを使用したほうが時間がかかることになる。
なのでトータルでの実行時間を速くするためにはThreadHandleを取得する時間が支配的にならないようなアプリで使ワなければ意味がない。

Programming Rust: Fast, Safe Systems Development

Programming Rust: Fast, Safe Systems Development

並行コンピューティング技法 ―実践マルチコア/マルチスレッドプログラミング

並行コンピューティング技法 ―実践マルチコア/マルチスレッドプログラミング

Ubuntu root file system for armhf

組み込みLinuxの勉強として
あるARMデバイスのためにSDカードのパーティションUbuntuのRoot File Systemを展開しておきたい。

タイトルはBuildとあるがビルドするほどのものでもないかもしれない。

実行環境はVMplayer上のUbuntu16.04 LTS

0 準備

$ sudo apt-get install qemu-user-static

今回はSDカードの第二パーティションにRoot File Systemを展開したいので事前にそうしておく。

$ sudo gparted /dev/sdb

などで
f:id:katakanan:20171224105057p:plain
とする

1 インストール

$ sudo mount -o loop /dev/sdX2  /mnt

ここを見て
Index of /dists
自分の欲しいUbuntuのバージョンの名前?をコマンドの適切な位置に入れる。
例えば
16.04 LTSならxenial
14.04 LTSならtrusty

sudo debootstrap --foreign --arch armhf [Ubuntuのバージョン] /mnt http://ports.ubuntu.com/
sudo cp /usr/bin/qemu-arm-static /mnt/usr/bin/
sudo chroot /mnt
/usr/bin/groups: cannot find name for group ID 0
I have no name!@ubuntu:/#

これでchrootした
ここからは、chroot以下での作業

I have no name!@ubuntu:/# ./debootstrap/debootstrap --second-stage
I have no name!@ubuntu:/# passwd
(略)
I have no name!@ubuntu:/# su
root@ubuntu:/# passwd (念のため)
(略)
root@ubuntu:/# apt-get update
(略)
root@ubuntu:/# adduesr hoge
(略:パスワードは設定する)

/home以下にhogeのフォルダができている。
または

root@ubuntu:/# cat /etc/passwd

hogeユーザーがいることを確かめて終了

これで一応Root File System の展開は終了。

・・続く

Linuxデバイスドライバ 第3版

Linuxデバイスドライバ 第3版