Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

MacBook Pro Late 2008でFFXIVベンチマーク

ファイナルファンタジーXIV オフィシャルベンチマーク
http://www.finalfantasyxiv.com/media/benchmark/jp/

9600M GTでは正直全く期待できないが……

ベンチマーク結果

画面解像度LOWで
SCORE=797
LOAD TIME=17046 ms

うーん、カックカックしてる。
やっぱこんなもんだよなぁ(;´~`)

――数日後。Direct Xやグラフィックドライバをずっと更新していなかったことに気付き、それらを更新してから実行してみた。

ベンチマーク結果2

SCORE=989
LOAD TIME=14004 ms

かくつきがいくらか軽減されていた。意外と差が出るものだ……

#pragma pack vs コンパイラ最適化

タグ: C++ VS2005

Visual Studio 2005のARMv4I向けコンパイラが構造体アライメントをどのように扱うか実験。

#pragma pack(push, 1)
// 1バイトアライメントの構造体
// sizeof(Hoge) == 10
struct Hoge
{
    short a; // 2バイト
    int b[2]; // 4バイト×2
};
#pragma pack(pop)

void zerohoge(Hoge& hoge);
void zeroint(int& n);
// 非インライン関数
void zerohoge(Hoge& hoge)
{
    // 3. 1バイトアライメントの構造体のデータメンバに値を代入する
    hoge.a = 0;
    hoge.b[0] = 0;
    hoge.b[1] = 0;
}

// 非インライン関数
void zeroint(int& n)
{
    // 4. 組み込み型に値を代入する
    n = 0;
}
void test()
{
    Hoge hoges[2];

    // 1. 1バイトアライメント構造体の4バイトのデータメンバが4バイト境界に整列されている
    hoges[1].a = 0;
    hoges[1].b[0] = 0;
    hoges[1].b[1] = 0;

    // 2. 1バイトアライメント構造体の4バイトのデータメンバが4バイト境界に整列されていない
    hoges[0].a = 0;
    hoges[0].b[0] = 0;
    hoges[0].b[1] = 0;

    // 3.a. 4バイト境界から始まる1バイトアライメントデータを渡す
    zerohoge(hoges[0]);

    // 3.b. 4バイト境界以外から始まる1バイトアライメントデータを渡す
    zerohoge(hoges[1]);

    // 4.a. 4バイト境界以外から始まる4バイトアライメントデータを渡す(!)
    zeroint(hoges[0].b[0]);

    Hoge* hoge = new Hoge;

    // 5.a 1バイトアライメント構造体のデータメンバにmemsetする
    ::memset(hoge->b, 0, sizeof(hoge->b));

    // memsetが最適化で消滅するのを防止するために非インライン関数呼び出しを行う
    zerohoge(*hoge);

    // 5.b 1バイトアライメント構造体のデータメンバではないとコンパイラに誤認させてmemsetする(!)
    ::memset(&hoge->b[0], 0, sizeof(hoge->b));

    // memset(ry
    zerohoge(*hoge);

    delete hoge;
}

このコードを/O2(実行速度最適化)でビルドし、要所要所「逆アセンブルを表示」しながら実行してみる。

    // 1. 1バイトアライメント構造体の4バイトのデータメンバが4バイト境界に整列されている
    hoges[1].a = 0;
    hoges[1].b[0] = 0;
    hoges[1].b[1] = 0;
00011008  mov         r3, #0 
0001100C  mov         r2, #0 
00011010  mov         r1, #0 
00011014  add         r0, sp, #0 
00011018  strh        r3, [sp, #0xA] 
0001101C  str         r2, [sp, #0xC] 
00011020  str         r1, [sp, #0x10] 

Hoge構造体のアライメントは1バイトだが、hoges[1]のデータメンバbはスタックメモリ上において4バイト境界に整列されることが確実なため、strh(2バイトストア)命令1つとstr(4バイトストア)命令2つという最少の命令数で全てのデータメンバに0を設定している。

    // 2. 1バイトアライメント構造体の4バイトのデータメンバが4バイト境界に整列されていない
    hoges[0].a = 0;
    hoges[0].b[0] = 0;
    hoges[0].b[1] = 0;
00011028  mov         lr, #0 
0001102C  mov         r4, #0 
00011030  mov         r3, lr, lsr #16 
00011034  mov         r2, r4, lsr #16 
00011038  mov         r1, #0 
0001103C  add         r0, sp, #0xA 
00011040  strh        r3, [sp, #4] 
00011044  strh        r2, [sp, #8] 
00011048  strh        r1, hoges 
0001104C  strh        lr, [sp, #2] 
00011050  strh        r4, [sp, #6] 

hoges[0]のデータメンバは、4バイト境界には整列されていないものの2バイト境界には揃えられているので、strh命令5つで0を設定している。

    // 3. 1バイトアライメントの構造体のデータメンバに値を代入する
    hoge.a = 0;
000110B8  mov         r3, #0 
000110BC  strb        r3, hoge 
000110C0  strb        r3, [r0, #1] 
    hoge.b[0] = 0;
000110C4  strb        r3, [r0, #2] 
000110C8  strb        r3, [r0, #3] 
000110CC  strb        r3, [r0, #4] 
000110D0  strb        r3, [r0, #5] 
    hoge.b[1] = 0;
000110D4  strb        r3, [r0, #6] 
000110D8  strb        r3, [r0, #7] 
000110DC  strb        r3, [r0, #8] 
000110E0  strb        r3, [r0, #9] 

zerohoge()の引数hogeは、コンパイル段階では偶数バイト奇数バイトどちらから始まるかも不明なので、どちらでも問題なく動作するようstrb(1バイトストア)命令10個で0を設定している。

    // 4. 組み込み型に値を代入する
    n = 0;
000110E8  mov         r3, #0 
000110EC  str         r3, n 

zeroint()の引数nは4バイトアライメントの組み込み型の参照型(=実引数が4バイト境界に整列されていることを呼び出し側が保証しなければならない)なので、str命令1つで0を設定する。
よって以下のように「4バイト境界に整列されていないデータメンバ」を渡すと、

    // 4.a. 4バイト境界以外から始まる4バイトアライメントデータを渡す(!)
    zeroint(hoges[0].b[0]);
00011058  add         r0, sp, #2 
0001105C  bl          |zeroint ( 110e8h )| 

ARMv4Iのような非境界整列アクセスに対応していない環境では問題が起こる。

Data Abort: Thread=8eff10a8 Proc=8c369c10 'test.exe'
AKY=00002001 PC=000110ec(test.exe+0x000010ec) RA=00011060(test.exe+0x00001060) BVA=1c02fe46 FSR=00000003
Unhandled exception at 0x000110ec in test.exe: 0x80000002: Datatype misalignment.

私の実行環境はここで落ちるので、とりあえずzeroint()の中身をコメントアウトして先に進める。

    Hoge* hoge = new Hoge;
00011060  mov         r0, #0xA 
00011064  bl          000110FC 
00011068  mov         r4, r0 

    // 5.a 1バイトアライメント構造体のデータメンバにmemsetする
    ::memset(hoge->b, 0, sizeof(hoge->b));
0001106C  add         r0, r4, #2 
00011070  mov         r2, #8 
00011074  mov         r1, #0 
00011078  bl          00011114 

ここは特に何の工夫もなく、普通にmemset()を呼んでいる。

    // 5.b 1バイトアライメント構造体のデータメンバではないとコンパイラに誤認させてmemsetする(!)
    ::memset(&hoge->b[0], 0, sizeof(hoge->b));
00011084  mov         r3, #0 
00011088  str         r3, [r4, #2] 
00011090  str         r3, [r4, #6] 

4バイト境界に整列されているはずの8バイトを0初期化するのにmemset()を呼ぶのも煩わしいと、str命令2個で片付けられている。
当然これは非境界整列アクセスになってしまうため、ARMv4Iでは問題になる。

Data Abort: Thread=8efe8658 Proc=8c369d00 'test.exe'
AKY=00004001 PC=00011088(test.exe+0x00001088) RA=00011084(test.exe+0x00001084) BVA=1e030062 FSR=00000003
Unhandled exception at 0x00011088 in test.exe: 0x80000002: Datatype misalignment.

「1バイトアライメントの領域を指すポインタ(参照)」から「4バイトアライメントの領域を指すポインタ(参照)」への変換は、本来reinterpret_cast並に気を配らなければならない行為である。
しかし構造体アライメントはC言語の仕様の範疇ではなく、その変換を明示的に行う方法(unpack_castとでも言おうか)も用意されていない。
コンパイラはコードを信じて「暗黙のunpack」を行うしかなく、プログラマはコンパイラが「誤解」しない書き方を把握しなければならない。

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。