Diary Soft Elec FM Doc Lib  

2008/10/14 火曜日

Atomの浮動小数演算性能

Filed under: 技術 — saw @ 21:42:23

ITmediaのこちらの記事に、Atomでは倍精度浮動小数はソフトウェアで処理されるという記述がありました。確かにx87系FPUには単精度・倍精度・拡張倍精度の3つの精度モードがありますが、普通に高級言語でソフトを作るとレジスタ間では常に倍精度か拡張倍精度で処理され、メモリアクセスの際にfloatなりdoubleなりに変換されるコードになったハズ(たぶん。。。)。となると、既存の小数を扱うソフトはそのままだとパフォーマンスが大幅に低下することになります。せっかくAtomでマシンを組んだので、実測してみました。
ベンチマークに使ったプログラムは以下の通り(VisualC++用)。同じレジスタにひたすら除算と乗算を繰り返しているだけなので、ベンチマークとしての信憑性はいまひとつですが、およその速度比率は求められるはずです。

#include <windows.h>
#include <stdio.h>

void main(void)
{
    __int64 freq, ctr1, ctr2;
    float f_a[2] = {1.23, 1.23}, f_b[2] = {4.56, 4.56};
    double d_a = 1.23, d_b = 4.56;
    const char *prec[] = {"single", "double", "long double"};
    unsigned short fcw[] = {0x007F, 0x027F, 0x037F};
    int i;

    QueryPerformanceFrequency((LARGE_INTEGER *)&freq);

    /* FPU 単精度、倍精度、拡張倍精度計測 */
    for (i = 0; i < 3; i ++) {
        QueryPerformanceCounter((LARGE_INTEGER *)&ctr1);
        __asm {
            push    ecx
            mov     ecx, i
            fldcw   word ptr [fcw + ecx*2]
            fld     d_b
            fld     d_a
            mov     ecx, 100000000
        fpu_loop:
            fdiv    st(0), st(1)
            fmul    st(0), st(1)
            loop    fpu_loop
            fst     d_a
            fst     d_b
            pop     ecx
        };
        QueryPerformanceCounter((LARGE_INTEGER *)&ctr2);
        printf("FPU %s: %fs\n", prec[i], (ctr2 - ctr1)*1.0 / freq);
    }

    /* SSE 単精度計測 */
    QueryPerformanceCounter((LARGE_INTEGER *)ctr1);
    __asm {
        push    ecx
        movlps  xmm0, f_a
        movhps  xmm0, f_a
        movlps  xmm1, f_b
        movhps  xmm1, f_b
        mov     ecx, 100000000
    sse_loop:
        divps   xmm0, xmm1
        mulps   xmm0, xmm1
        loop    sse_loop
        pop     ecx
    };
    QueryPerformanceCounter((LARGE_INTEGER *)&ctr2);
    printf("SSE: %fs.\\n", (ctr2 - ctr1)*1.0 / freq);

    /* SSE2 倍精度計測 */
    QueryPerformanceCounter((LARGE_INTEGER *)&ctr1);
    __asm {
        push    ecx
        movlpd  xmm0, d_a
        movhpd  xmm0, d_a
        movlpd  xmm1, d_b
        movhpd  xmm1, d_b
        mov     ecx, 100000000
    sse2_loop:
        divpd   xmm0, xmm1
        mulpd   xmm0, xmm1
        loop    sse2_loop
        pop     ecx
    };
    QueryPerformanceCounter((LARGE_INTEGER *)&ctr2);
    printf("SSE2: %fs.\\n", (ctr2 - ctr1)*1.0 / freq);
}

結果。%はそれぞれのFPU単精度の時間を100%とした相対値です。コアごとの傾向を見るためのものなので、絶対値は気にしないでください。

Atom(Diamondville) Core2(Conroe) CeleronD(Prescot) PentiumM(Banias)
FPU(単精度) 2.3870秒(100%) 1.0173秒(100%) 1.6001秒(100%) 2.3345秒(100%)
FPU(倍精度) 4.2026秒(180%) 1.6070秒(158%) 1.9201秒(120%) 3.7526秒(161%)
FPU(拡張倍精度) 4.8927秒(205%) 1.8656秒(183%) 2.1215秒(134%) 4.3684秒(187%)
SSE(単精度x4) 9.4088秒(394%) 0.9540秒(94%) 4.0408秒(253%) 7.8163秒(335%)
SSE2(倍精度x2) 8.6567秒(363%) 1.6052秒(158%) 3.1251秒(195%) 6.2911秒(269%)

確かにAtomはFPU倍精度が遅いですが、他と比べて大幅に遅いわけでもなく、ソフト処理していると思わせるほどではありません。というか、ソフトでトラップ処理する場合OSのサポートが必要になってしまうので、ソフトウェアで処理するというのはマイクロコードによる実装比が高いといった意味合いかもしれませんね。また、単精度・倍精度問わず、SSEの遅さが光ります。ヘタをするとFPUで普通でやる場合に負けるかも。Core2とは対照的です。
なお、VisualC++の場合、float、doubleの使用状況にかかわらず倍精度モードで演算されるバイナリになるようです(拡張倍精度はサポートされていない)。SSE2未対応のため表にありませんが、手持ちのCPUで単位クロックあたりのスコアがもっとも優秀だったのはAthlonXPでした。

2008/10/13 月曜日

D945GCLF2購入

Filed under: 雑記 — saw @ 20:17:53

物欲に負けて買ってしまいました。HDDやケースまでそろえたのでそれなりに高い買い物に。いまだにJTAGなどでパラレルポートを使うことがあるので、Mini-ITXながらレガシーデバイスが充実しているのはうれしいですね。ただチップセットについているファン(0.18A)が少々うるさめ。0.09Aのものに交換しましたが、机の上に置くとまだ少し気になります。小型のMini-ITXケースに入れるなら、ファンは横から風を当てるように配置しなおしたほうが効率よさそうです。
性能に関しては随所で報告されているとおりなので、2.5インチHDD+ACアダプタ電源の組み合わせでの消費電力を計ってみました。

・アイドル時33W
・while(1);×4プロセスでCPU使用率100%時35W
・3DMark06実行時40W

Core2Duo+オンボードビデオでもHDDと電源が同じ構成ならアイドル40W切れそうなので、本格的に使うのならこちらで組むか、省エネ目的ならEeeBoxかノートPCにしてしまったほうがいいかもしれません(コストは若干上がりますけど)。
おもしろかったのは、Atom330は2コア×HTの仮想4コアですが、while(1);1スレッドあたりほぼ25%ずつしかCPUを占有しなかったこと。単なる空ループならHTで完全に並列実行できるようです。ループの中に何か処理を入れると内容に応じて並列実行率が下がり、CPU占有率が上がっていくものと思われます。

2008/10/11 土曜日

M16Cのポインタ型サイズ

Filed under: プログラミング — saw @ 23:55:35

M16Cは典型的な16ビットCPUなのでnearとfarの2種類のポインタ型を持っています。純正コンパイラの場合、_nearまたは_far修飾子を付けることで明示的に指定できますが、特に指定しなかった場合のデフォルトのサイズがconstと非constで異なるようです。

int *ptr;       /* near型(16ビット) */
const int *cptr; /* far型(32ビット) */

ポインタへのconst指定はコンパイラの構文チェックだけでバイナリには影響しないと思っていたので意外。しかしコンパイラのマニュアルにちゃんとconstはfar属性になると書いてあります。考えてみると、非constなポインタはRAMしか指せませんが、constポインタはROM/RAM両方指すことができて、M16Cの場合RAMは最下位アドレス、ROMは最上位アドレスに配置されていますから、確かに理にかなった挙動です。でもconstにするとパフォーマンスが落ちるのが悩ましい…
同じルネサスの16ビットマイコンでも、H8(H8/300H)はレジスタが32ビットなのでこのへんのややこしさはありません。

2008/10/9 木曜日

OAKS16ボードでTOPPERS/JSP

Filed under: プログラミング — saw @ 23:42:43

M16Cには一般の割り込みとCPU例外の2つのベクタがありますが、これらを区別してハンドラを登録する方法がわからず悩んでいました。ドキュメントによるとDEF_INHで割り込みハンドラ、DEF_EXCで例外ハンドラの登録となるようですが、実際にはどちらで定義しても割り込みのほうに登録されてしまいます。が、コンフィギュレータのソースを見る限りちゃんとINHとEXCで区別されるように書かれています。どうやら構文解析器のバグのよう。2つのハンドラグループはコンフィギュレータの中間ファイルに以下のように定義されますが、この中間ファイルを処理するm16cvec.exeが閉じ括弧の};を認識せず、すべてINHINIBで処理されてしまっています。

const INHINIB inhinib_table[TNUM_INHNO] = {
...
};
const EXCINIB excinib_table[TNUM_EXCNO] = {
...
};

以下のように修正。

m16cvec.cpp
…
   switch(vec_state){
   case INT_STATE:
   case EXC_STATE:
-    if(test_string(&s, ";"))
+    if(test_string(&s, "};"))
       vec_state = NORMAL_STATE;
     else if(test_string(&s, "{")){
       v = &vec_table[vec_state][num_vec[vec_state]];
       p = &v->no_name[0];
…

ちなみに登録したかったのはウォッチドッグタイマのハンドラです。ウォッチドッグはタイムアウトすると問答無用でリセットするのが一般的ですが、このボードに使われているM16C/62Aは通常の割り込み扱いになっているため(ただし割禁でマスクされない)、ハンドラで明示的にリセットさせなければなりません。改良版のM16C/62Pは両方の方式を選べるようです。

2008/9/22 月曜日

Atom330 D945GCLF2発売

Filed under: 未分類 — saw @ 22:05:09

8月の段階で発表はされていたようですが、意外にデュアルコア化早かったですね。
デュアルコアAtomマザーが発売に、1.1万円
ってこれはデュアルコアと言うよりデュアルプロセッサでは…
TDPもしっかり倍増しているようですし。どうりで出るの早いと。
まあ、4W→8Wに倍増してもPC全体から見ればわずかな差だし、むしろシングルのAtomが全体に対するCPUの消費電力の割合が低すぎて損してる感があったので、これくらいが丁度いいのかも。スペック的にもゲーム用途でなければ十分実用になりそう。
DVI搭載モデルが出ないかなー。

2008/9/13 土曜日

SEG CLIPのリソースリーク

Filed under: 雑記 — saw @ 22:13:58

しばらく前から、2日ほどPCをつけっぱなしにしておくとウィンドウのパーツが欠落したりフォントが表示されなくなる現象が出ていたんですが、タスクマネージャで見えるハンドル数がどんどん増えていることに気がつきました(OSはWindows XP)。IO-DATAのワンセグチューナ、GV-SC200の制御アプリであるSEG CLIP(OneSegPc.exe)が毎秒きっちり2個ずつハンドルをリークしています。Filemonで挙動を確認すると、なにやら定期的にファイルアクセスをしていますが、そのときにファイルハンドルの1つをCloseし忘れているようです。GV-SC300のは直っていると思いますが、200用は300発売と同時に更新が止まっています。どうせ大部分が共通なんだから、両方直してくれても良さそうなのに。まあ、常駐させなければいいんですけど。

ただ、リソースリークはともかく、ウィンドウの欠落が起こるというのが謎。デスクトップヒープが足りなくなるとそうなるようですが、その場合はエラーメッセージが出るはずだし、ファイルハンドル関係ないし。別件かもしれませんね。そろそろ再インストールかな…

2008/8/22 金曜日

SCE、液晶ディスプレイを強化した新型PSP「PSP-3000」-10月発売

Filed under: 雑記 — saw @ 3:48:51

な、なんだってー
買ったばっかりじゃないか…
しばらくはPSP-2000と併売らしいので少し高いんでしょうけど。
DSLiteみたいなものですか。

2008/8/14 木曜日

ARMのR12レジスタ

Filed under: ARM Cortex-M, プログラミング — saw @ 23:55:59

これって関数呼び出しの際に保存されないのね。
破壊されるのはR0~R3とLRだけかと思っていました。
確かに割り込み時に自動保存されるようになっているし、スクラッチレジスタと書いてある。
アセンブラからCの関数を呼んだらコードを少し変えただけで動いたり動かなかったりして、ちょっと悩みました。

2008/8/13 水曜日

PSP(PSP-2000)を買ってみた

Filed under: 雑記 — saw @ 21:19:51

早速不良画素を1つ発見してしまいました。
不良画素というより、画素と画素の隙間から光が漏れてる感じ?顕微鏡か何かで見ないとわかりませんが、画素そのものは正しく発色しているように見えます。
輝度はかなり低いので、何か映っていればわからないんですけど、画面切り替えの際にどうしてもそこに目がいってしまいますね(^^;。
それにしても、今までDS2台、PSP1台購入していずれも不良画素が1個ずつ。PC用ディスプレイでは、XGA4台、SXGA2台、WUXGA1台で当たりなし(ノートPCで常時点灯が1つあったけれど、パネルに力を加えると出たり消えたりして、最終的に消えるほうで落ち着いた)。画素あたりの不良率が同じならPC用のほうが圧倒的に発生率が高いはずなんですが、結果は完全に逆。PC用のほうが高度な技術で生産or検査しているんですかね。値段を考えると当然かもしれませんが。

ところで、仕事で使っている会社の液晶にもかなり目立つ位置に赤と青の常時点灯が1つずつありますが、これはまったく気になりません。やはり、実害があるというより、自分の買ったものに欠陥があること自体が気になるようで…。
この仕事液晶も、赤いほうはつつくと出たり消えたり。液晶には詳しくありませんが、画素そのものに問題がある場合と、配線が切れかかってる場合があるのかな?

2008/5/15 木曜日

C/アセンブラからC++のメンバ関数を呼び出す

Filed under: プログラミング — saw @ 22:39:49

アセンブラのコード中からC++のクラスメンバ関数を呼ぶ必要が発生したので、やり方を調べてみました。C++のメンバ関数の呼び出し規約(thiscall)は処理系によって異なるようですが、Win32の場合は以下のようになっています。

  • thisポインタはECXレジスタで渡される
  • 引数は右から左の順番でスタックに積まれる
  • スタックに積んだ引数のクリアは、固定長引数の場合は関数側、可変長引数の場合は呼び出し側で行う
  • 戻り値はstdcall等と同じ(32ビット以内ならEAX)
  • EBX,EBP,ESI,EDIは保存される。EAX,ECX,EDXは保証されない。

実験。

test1.cpp
#include <stdio.h>

// サンプルのクラス
class TESTCLASS
{
public:
  int TestFunc(int, int);
  int m_Int;
};

int TESTCLASS::TestFunc(int arg1, int arg2)
{
  printf("TestFunc(%d, %d) m_Int = %d\n",arg1, arg2, m_Int);

  return arg1 + arg2;
}

// メンバ関数へのポインタをexport
extern "C"
{
  int (TESTCLASS::*g_pfunc)(int, int);
  void TestCFunc(void *);
}

int main(void)
{
  TESTCLASS inst;

  // メンバ変数にダミーの値をセット
  inst.m_Int = 123;

  // メンバ関数へのポインタ取得
  g_pfunc = &TESTCLASS::TestFunc;

  // Cの関数コール
  TestCFunc(&inst);

  return 0;
}
test2.c
#include <stdio.h>

/* クラスのメンバ関数へのポインタ。
   リンクが通ればいいので、ポインタ型ならなんでもよい */
extern void *g_pfunc;

void TestCFunc(int *pinst)
{
  int retvalue;

  __asm {
    /* EAX,ECX,EDXが破壊されるので、必要なら保存 */
    push  eax
    push  ecx
    push  edx

    /* retvalue = pinst->TestFunc(1, 2) */
    mov   ecx,pinst /* インスタンス */
    push  2         /* 第2引数 */
    push  1         /* 第1引数 */
    call  g_pfunc
    mov   retvalue, eax

    /* 待避したレジスタを復元 */
    pop   edx
    pop   ecx
    pop   eax
  }

  printf("return: %d\n", retvalue);
}

結果

TestFunc(1, 2) m_Int = 123
return: 3

問題ないようです。CからではthisポインタをECXに渡す指定ができないので、インラインアセンブラを使うしかありません。前述の通り互換性がないので、この呼び方はWindows専用です。
この例では呼ぶべき関数のポインタをあらかじめ取得してありますが、仮想関数をやろうとすると一気に面倒なことになります。C及びアセンブラのコードからクラス宣言が参照できない以上、テーブルの何番目が目的の関数かがわからないので、マジックナンバーを使うしかないでしょう。

次のページ »