無線
EM-X270にはBluetooth WiFi GSM GPS、と至れリつくせりの無線デバイスが搭載可能だが、考えてみればこれはちょっとヤバいかもしれない。日本の電波法ではこの手の機器はTELEC認定されなければ使用できないのだが、Compulabの日本法人はまるっきりやる気が無いし、イスラエルで開発された基盤が日本とシンガポールでしか通用しないTELEC認定を受けているとは思えない。公式サイトには全く書かれていないが現物の写真の無線モジュールの表面に見えるFCC IDと、Reference GuideのWiFiの部分に書かれているwhile still staying compliant with FCC regulations.(FCCの定める基準に従う限り)というあたりからFCC認定は受けているものと思われる。
日本国内で無線端末としてEM-X270を使うために買うのはまずいアイデアかもしれない。
日本国内で無線端末としてEM-X270を使うために買うのはまずいアイデアかもしれない。
ダイナミックロードの隠蔽(PowerPC版)
PowerPCもダイナミックロードの隠蔽。
PowerPC版LinuxはSystem V R4 ABI PowerPC editionに準拠している。アセンブラと高級言語の間でやりとりする場合はこのドキュメントを読んでおいた方が良いだろう。PowerPC自体の仕様はここらへんに落ちている。読んでもよくわからなかったり、読むのがめんどくさかったりする場合はJun's Homepageさんの玄箱でアセンブリが役に立ちそう。
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <dlfcn.h>
extern "C" void initSqrt();
namespace sample_namespace {
double (*sqrt)( double ) = (double (*)( double ))(void*)( &initSqrt );
}
double (*__temp_sqrt)( double ) = NULL;
extern "C" void loadSqrt() throw( std::domain_error ) {
void *handle = dlopen( "libm.so", RTLD_LAZY );
if( !handle )throw std::domain_error( "Unable to load libm" );
sample_namespace::sqrt = (double (*)( double ))dlsym( handle, "sqrt" );
if( !sample_namespace::sqrt ) {
sample_namespace::sqrt = (double (*)( double ))(void*)( &initSqrt );
dlclose( handle );
throw std::domain_error( "Unable to find sqrt" );
}
__temp_sqrt = sample_namespace::sqrt;
}
__asm__ __volatile__(
" .section .text\n"
"initSqrt: mflr 11\n"
" stwu 11, -4(1)\n"
" bl loadSqrt\n"
" lwz 11, 0(1)\n"
" addi 1, 1, 4\n"
" mtlr 11\n"
" lis 12, __temp_sqrt@ha\n"
" lwz 11, __temp_sqrt@l(12)\n"
" stw 0, __temp_sqrt@l(12)\n"
" mtctr 11\n"
" bctr\n"
" blr"
);
int main() {
std::cout << sample_namespace::sqrt( 2.0 ) << std::endl;
exit( EXIT_SUCCESS );
}
PowerPC版LinuxはSystem V R4 ABI PowerPC editionに準拠している。アセンブラと高級言語の間でやりとりする場合はこのドキュメントを読んでおいた方が良いだろう。PowerPC自体の仕様はここらへんに落ちている。読んでもよくわからなかったり、読むのがめんどくさかったりする場合はJun's Homepageさんの玄箱でアセンブリが役に立ちそう。
ダイナミックロードの隠蔽
面白いモノが出来た。
このプログラムは数学ライブラリ内のsqrt関数をダイナミックロードして2.0の根を求めるというものだが、main関数にはsqrt関数を呼び出す前にダイナミックロードをしたような部分は無い。sqrt関数のアドレスを入れておくポインタには予め真ん中あたりのアセンブラコードinitSqrtのアドレスが入っている。このアセンブラコードはまず呼び出した直後のスタックポインタとベースポインタを外の変数に書き出してからベースポインタをスタックポインタの位置にして、sqrtをダイナミックロードする関数loadSqrtを呼び出してから__sqrtCallerにジャンプする。__sqrtCallerはまずsqrt関数のアドレスをアセンブラから読める位置に移し、initSqrtの最初に外の変数に書き出しておいたスタックポインタとベースポインタの値で現在のスタックポインタとベースポインタを上書きする。こうしてスタックがinitSqrt呼び出し時点まで巻き戻され、initSqrtにわたされた引数がsqrt関数に渡され、返り値は__sqrtCallerではなくmainに返ってくる。
こんなまわりくどいことをして何がうれしいかというと、ダイナミックロードが行われたという事実をほぼ完全に呼び出し側から隠蔽する事が出来る、ということだ。一応ダイナミックロードは失敗する事があり、その場合例外が飛ぶようにしてあるのでそれを想定した対応は必要だが、その点をのぞいてあたかも最初からそこに関数があったかのように使用する事が出来る。
__sqrtCallerを通して呼び出しているのはネームスペースの内側やインスタンスが保持しているマングルされた関数ポインタを使えるようにするため。アセンブラとC++の組み合わせはあんまり幸せになれないなー。
当然の事ながらx86専用。
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <dlfcn.h>
extern "C" void initSqrt();
namespace sample_namespace {
double (*sqrt)( double ) = (double (*)( double ))(void*)( &initSqrt );
}
extern "C" void __sqrtCaller() {
register void *temp asm("%eax") = (void*)sample_namespace::sqrt;
__asm__ (
" mov external_ebp, %ebp\n"
" mov external_esp, %esp\n"
" jmp *%eax"
);
}
extern "C" void loadSqrt() throw( std::domain_error ) {
void *handle = dlopen( "libm.so", RTLD_LAZY );
if( !handle )throw std::domain_error( "Unable to load libm" );
sample_namespace::sqrt = (double (*)( double ))dlsym( handle, "sqrt" );
if( !sample_namespace::sqrt ) {
sample_namespace::sqrt = (double (*)( double ))(void*)( &initSqrt );
dlclose( handle );
throw std::domain_error( "Unable to find sqrt" );
}
}
__asm__ (
" .section .bss\n"
" .align 4\n"
"external_ebp: .type external_ebp, @object\n"
" .size external_ebp, 4\n"
"external_esp: .type external_esp, @object\n"
" .size external_esp, 4\n"
" .section .text\n"
"initSqrt: mov %ebp, external_ebp\n"
" mov %esp, external_esp\n"
" mov %esp, %ebp\n"
" call loadSqrt\n"
" jmp __sqrtCaller"
);
int main() {
std::cout << sample_namespace::sqrt( 2.0 ) << std::endl;
exit( EXIT_SUCCESS );
}
このプログラムは数学ライブラリ内のsqrt関数をダイナミックロードして2.0の根を求めるというものだが、main関数にはsqrt関数を呼び出す前にダイナミックロードをしたような部分は無い。sqrt関数のアドレスを入れておくポインタには予め真ん中あたりのアセンブラコードinitSqrtのアドレスが入っている。このアセンブラコードはまず呼び出した直後のスタックポインタとベースポインタを外の変数に書き出してからベースポインタをスタックポインタの位置にして、sqrtをダイナミックロードする関数loadSqrtを呼び出してから__sqrtCallerにジャンプする。__sqrtCallerはまずsqrt関数のアドレスをアセンブラから読める位置に移し、initSqrtの最初に外の変数に書き出しておいたスタックポインタとベースポインタの値で現在のスタックポインタとベースポインタを上書きする。こうしてスタックがinitSqrt呼び出し時点まで巻き戻され、initSqrtにわたされた引数がsqrt関数に渡され、返り値は__sqrtCallerではなくmainに返ってくる。
こんなまわりくどいことをして何がうれしいかというと、ダイナミックロードが行われたという事実をほぼ完全に呼び出し側から隠蔽する事が出来る、ということだ。一応ダイナミックロードは失敗する事があり、その場合例外が飛ぶようにしてあるのでそれを想定した対応は必要だが、その点をのぞいてあたかも最初からそこに関数があったかのように使用する事が出来る。
__sqrtCallerを通して呼び出しているのはネームスペースの内側やインスタンスが保持しているマングルされた関数ポインタを使えるようにするため。アセンブラとC++の組み合わせはあんまり幸せになれないなー。
当然の事ながらx86専用。
最強のハンドヘルドLinuxボード入手のチャンス
以前紹介したCompuLab社の高性能VGA液晶付きボードコンピュータEM-X270、最大の難点は個人向けに小ロットでは販売してくれない事だった。しかし、諦めきれないのは自分だけではなかったらしく
The Handheld Linux Shopが購入希望者を100人集めてまとめて購入しようというのをやっているようだ。個人でEM-X270を手に入れる貴重なチャンスだ!手のひらサイズでタッチパネル液晶をはじめとする多彩なデバイスを搭載し、Armadillo-500を上回る驚異のスペックを持つLinux環境を手に入れたい人はこれを見逃すわけにはいかないだろう。しかしお値段1台649ユーロ、日本円で約10万円、送料込で11万円弱。た、高ぁ!
どうする
どうする
どうする
ちなみに注文の期限は2008年1月31日まで、現在の注文数は9個で期限までに100個に達したら注文される。発送予定日は2008年2月31日という不思議な日付が書かれている。
The Handheld Linux Shopが購入希望者を100人集めてまとめて購入しようというのをやっているようだ。個人でEM-X270を手に入れる貴重なチャンスだ!手のひらサイズでタッチパネル液晶をはじめとする多彩なデバイスを搭載し、Armadillo-500を上回る驚異のスペックを持つLinux環境を手に入れたい人はこれを見逃すわけにはいかないだろう。しかしお値段1台649ユーロ、日本円で約10万円、送料込で11万円弱。た、高ぁ!
どうする
どうする
どうする
ちなみに注文の期限は2008年1月31日まで、現在の注文数は9個で期限までに100個に達したら注文される。発送予定日は2008年2月31日という不思議な日付が書かれている。
それぞれの浮動小数点数の計算方法のパフォーマンス
精度の次は速さが気になってきたので簡単な比較をしてみた。
今回試すソースはこんな感じ
比較結果
至極予想通りの結果が出た。ぅーん、やっぱり-ffloat-store遅いなぁ。
最後のものはsseと387の両方を使ってリソースを無駄なく使うオプション。しかし、実際にはsseだけ使用した場合と殆んど同じ結果が出たので、逆アセンブルしてみたところfpuは使っていなかった。個人的にはこんな小さな計算のなかでfpuとsseの間で値の移動を繰り返して、そのオーバーヘッドでかえって遅くなった!みたいな展開を期待したのだが、コンパイラは真っ当な判断をしたようだ。
今回試すソースはこんな感じ
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main() {
float hoge = 1.0e-9;
float piyo = 3.0;
int counter;
for( counter = 0; counter != 100000000; counter++ )
hoge = (double)sqrtf( piyo * piyo - 1.0f / hoge ) + 1.0;
}
比較結果
$ gcc test3.c -mfpmath=387 -ffast-math -funroll-all-loops
$ time ./a.out
real 0m4.960s
user 0m4.910s
sys 0m0.004s
$ gcc test3.c -msse -mfpmath=sse -ffast-math -funroll-all-loops
$ time ./a.out
real 0m4.187s
user 0m4.142s
sys 0m0.002s
$ gcc test3.c -msse2 -mfpmath=sse -ffast-math -funroll-all-loops
$ time ./a.out
real 0m3.971s
user 0m3.927s
sys 0m0.000s
$ gcc test3.c -msse3 -mfpmath=sse -ffast-math -funroll-all-loops
$ time ./a.out
real 0m3.695s
user 0m3.651s
sys 0m0.004s
$ gcc test3.c -mfpmath=387 -ffast-math -ffloat-store -funroll-all-loops
$ time ./a.out
real 0m6.451s
user 0m6.412s
sys 0m0.003s
$ gcc test3.c -msse3 -mfpmath=sse,387 -ffast-math -funroll-all-loops
$ time ./a.out
real 0m3.696s
user 0m3.661s
sys 0m0.003s
$ time ./a.out
real 0m4.960s
user 0m4.910s
sys 0m0.004s
$ gcc test3.c -msse -mfpmath=sse -ffast-math -funroll-all-loops
$ time ./a.out
real 0m4.187s
user 0m4.142s
sys 0m0.002s
$ gcc test3.c -msse2 -mfpmath=sse -ffast-math -funroll-all-loops
$ time ./a.out
real 0m3.971s
user 0m3.927s
sys 0m0.000s
$ gcc test3.c -msse3 -mfpmath=sse -ffast-math -funroll-all-loops
$ time ./a.out
real 0m3.695s
user 0m3.651s
sys 0m0.004s
$ gcc test3.c -mfpmath=387 -ffast-math -ffloat-store -funroll-all-loops
$ time ./a.out
real 0m6.451s
user 0m6.412s
sys 0m0.003s
$ gcc test3.c -msse3 -mfpmath=sse,387 -ffast-math -funroll-all-loops
$ time ./a.out
real 0m3.696s
user 0m3.661s
sys 0m0.003s
至極予想通りの結果が出た。ぅーん、やっぱり-ffloat-store遅いなぁ。
最後のものはsseと387の両方を使ってリソースを無駄なく使うオプション。しかし、実際にはsseだけ使用した場合と殆んど同じ結果が出たので、逆アセンブルしてみたところfpuは使っていなかった。個人的にはこんな小さな計算のなかでfpuとsseの間で値の移動を繰り返して、そのオーバーヘッドでかえって遅くなった!みたいな展開を期待したのだが、コンパイラは真っ当な判断をしたようだ。