またレイテンシ測るためだけに…の続編

まあ…A8-3650が手元にあるので…とりあえず…測った…(のは10日ほど前)
計測コードはこれと一緒

ちなみに、あの後もうちょっと調べて、どうやら、バックグラウンドで何か処理してCPU時間を使っておくと、レイテンシが縮むらしい、ということがわかったので、バックグラウンドのMeadowで(while t)してる状態で測った。

  • empty latency: 85usecぐらい
  • read 1byte: 200usecぐらい

という感じであった。

ちなみに同じ条件でE350測ると、

  • empty latency: 150usec
  • read 1byte: 500usec

ぐらいだった。


A8-3650で、

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

HANDLE s2c;
HANDLE c2s;

void __cdecl
client(void *p)
{
    while (1) {
        WaitForSingleObject(s2c, INFINITE);
        ResetEvent(s2c);
        SetEvent(c2s);
    }
}

void __cdecl
server(void *p)
{
    while (1) {
        unsigned int b, e;
        getchar();

        b = __rdtsc();
        SetEvent(s2c);
        WaitForSingleObject(c2s, INFINITE);
        e = __rdtsc();
        ResetEvent(c2s);

        printf("%d\n", e-b);
    }
}

volatile int s2cM;
volatile int c2sM;

void __cdecl
clientM(void *p)
{
    while (1) {
        while (s2cM == 0)
            ;
        s2cM = 0;
        c2sM = 1;
    }
}

void __cdecl
serverM(void *p)
{
    while (1) {
        unsigned int b, e;
        getchar();

        b = __rdtsc();
        s2cM = 1;
        while (c2sM == 0)
            ;
        e = __rdtsc();
        c2sM = 0;

        printf("%d\n", e-b);
    }
}


int
main(int argc, char **argv)
{
    s2c = CreateEvent(NULL, TRUE, FALSE, NULL);
    c2s = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (argc < 2) {
        _beginthread(client, 0, 0);
        _beginthread(server, 0, 0);
    } else {
        _beginthread(clientM, 0, 0);
        _beginthread(serverM, 0, 0);
    }

    Sleep(10000);
}

とかやって、Win32スレッドの通信レイテンシ測ると、何かプロセッサが動いてるときは4万clk、全プロセッサが寝ているときは、6万clkぐらい。で、2.6GHzだから…15usecぐらいか。
まあ、つまり、Win32スレッド間通信の4〜5倍くらいの時間でGPUと通信できていることになる。と、考えると、まあ、ギリギリ真面目に使えるか、と、いう感じ?1msecぐらいの処理なら、適用できそうという雰囲気。


あとメモリがのレイテンシがまだ200usecとかで大きいなぁ。

OpenCLは、メモリ共有してるGPUのために、ホストのメモリを直接GPUに渡すというのがサポートされていて、clCreateBuffer するときに、CL_MEM_USE_HOST_PTRとかCL_ALLOC_HOST_PTRとか渡せばよい。


が、今のAMD Fusion(llanoとzacateの両方)は、CPUとGPUが自由に同じ領域に読み書きできるようにはなっていない、具体的には多分CPUとGPUでコヒーレンシとってなくて、GPUからキャッシュ領域に高速で読み書きすることができないようになっている。
表にまとめると、

メモリの種類 GPU CPU
1.普通のページ(CL_USE_HOST_PTR:ロックされてなくてスワップするかも) 何もできない 普通に使える
2.ロックされてキャッシュ可能なページ(CL_ALLOC_HOST_PTR) 読み書きゆっくり 普通に使える
3.WriteCombinedでキャッシュ不可 普通に使える 読みゆっくり/シーケンシャルな書きは普通
4.GPU領域(普通にclCreateBuffer) 普通に使える 使えない

となっている。


で、3番目の、「WCでッキャッシュ不可」、をOpenCLで表現できないので、AMDOpenCLは拡張が入っていて、CL_MEM_USE_PERSISTENT_MEM_AMDというフラグがある。


CL_MEM_USE_PERSISTENT_MEM_AMDを付けて、メモリ確保すると、「ページスワップとか移動しなくてキャッシュ不可でWC」なメモリが割り当てられる。このメモリは、GGPGPU的には以下のような特性を持つ

  • GPUで読み書きするのはフルスピードでアクセスできる(キャッシュ気にしないでよいので)
  • CPUで読むのはすっごい遅くなる。(キャッシュ不可になると、読み込み単位がワード単位になって、バースト転送効かないので)
  • CPUでシーケンシャルに書き込むのは、そこそこのスピードで実現できる(書き込みはWCが効くので、64byteのバースト転送になる)AMDのサンプルに入っている、BufferBandwidth で -I とか -O の値を変えると、このへんの変化が見られる。


これをうまく使えば、CPU<->GPU間のデータ転送を最小限にすることができる。

  1. ファイルから大量のデータ読み
  2. ちょっと加工してGPUへ送る
  3. GPUで処理
  4. 結果をホストへ返す
  5. 結果をホストで表示


と、いう処理をしたとして、このとき、
2. で加工するときに結果をWCメモリに置く、んで、GPUから返すデータはなるべく小さくなるようにする。
と、無駄な転送が無くなる。
(あとはSSE4.1の_mm_stream_load_si128みたいなのが欲しいところである)


まあ、めんどいすぎるけど…はやくキャッシュ領域に自由に読み書きできるようになってほしいところである。

あ。よく考えたらCUDAのWCメモリと全く同じ物だった。