質問

概要:

memcpyのような譲渡2GB/秒の私のシステムの実際またはテスト用です。めにどうしたらいいのだろうかく高速メモリーからメモリにコピー?

詳細な情報:

の一環として、データ取得の申請(一部の特殊なハードウェアながらプレーする必要がありまコピーを約3GB/secからの臨時バッファ。取得データのハードドライバーシリーズのバッファー(2MBます。ハードウェアの有のdmaをデータ各バッファーとして知私のプログラムそれぞれのバッファがいっぱい。私のプログラムが空には、バッファ(memcpyも、大きなブロックRAM)、および会員の方はログインの処理にバッファのカードに入ります。私はこの問題memcpy動のデータ速度です。このメモリをメモリにコピー速に十分な支援3GB/secのハードウェアがいます。Lavalys互いを与えられることになる9337MB/secメモリコピーのベンチマーク結果を出すことはできなかったがいつも近くその速度がmemcpyでも、簡単なテストプログラム。

私は分離のパフォーマンスの問題を追加取り付けたり取り外したりするのmemcpy電話のバッファ処理コードです。なmemcpyんで"ブレクスピプラゾール"を率い3GB/sec.のmemcpy、私は限られて550Mb/sec(使用電流コンパイラ).

するためのベンチマークmemcpyに私のシステムの立ちたいと思っていま別の試験プログラムを呼び出すだけですmemcpy一部のブロックのデータです。(私を掲載し以下のコードを思うこともにコンパイラ/IDEを使用してい(National Instruments CVI)とVisual Studio2010.私は現在使用してVisual Studioしていきたいと思い、スイッチの場合の利益をもたらすのに必要なす。しかし、盲目的移したかったのを確認することを解決私memcpy能です。

Visual C++2010:1900年MB/sec

NI CVI2009年550MB/sec

なんと驚くCVIは大幅に遅Visual Studioことに驚いていmemcpy性能が低い。なくなった場合は直接的に比較できる、この生するためには、エベレストベンチマーク帯域幅。らせんを必要とするであろうレベルの性能、最小限の3GB/secが必要です。確かに標準ライブラリの実装がこんなに可もないようエベレストは使用しています!●

何が何でもできませmemcpyよりも早いスピードです。


ハードウェアの詳細:AMD買Cours-4倍速octalコア 128GB DDR3 Windows Server2003企業X64

試験プログラム:

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

const size_t NUM_ELEMENTS = 2*1024 * 1024;
const size_t ITERATIONS = 10000;

int main (int argc, char *argv[])
{
    LARGE_INTEGER start, stop, frequency;

    QueryPerformanceFrequency(&frequency);

    unsigned short * src = (unsigned short *) malloc(sizeof(unsigned short) * NUM_ELEMENTS);
    unsigned short * dest = (unsigned short *) malloc(sizeof(unsigned short) * NUM_ELEMENTS);

    for(int ctr = 0; ctr < NUM_ELEMENTS; ctr++)
    {
        src[ctr] = rand();
    }

    QueryPerformanceCounter(&start);

    for(int iter = 0; iter < ITERATIONS; iter++)
        memcpy(dest, src, NUM_ELEMENTS * sizeof(unsigned short));

    QueryPerformanceCounter(&stop);

    __int64 duration = stop.QuadPart - start.QuadPart;

    double duration_d = (double)duration / (double) frequency.QuadPart;

    double bytes_sec = (ITERATIONS * (NUM_ELEMENTS/1024/1024) * sizeof(unsigned short)) / duration_d;

    printf("Duration: %.5lfs for %d iterations, %.3lfMB/sec\n", duration_d, ITERATIONS, bytes_sec);

    free(src);
    free(dest);

    getchar();

    return 0;
}

編集:だい五分の役に立ちたいと思い、実行上記のコードを利用するマシン後のお時間としてのコメント?

役に立ちましたか?

解決

この状況で速度を上げる方法を見つけました。 Memcpyのマルチスレッドバージョンを書き、スレッド間でコピーする領域を分割しました。上記と同じタイミングコードを使用して、セットブロックサイズのパフォーマンススケーリング番号を以下に示します。特にこの小さなサイズのブロックでは、この多くのスレッドにパフォーマンスがスケーリングされるとは思いもしませんでした。これは、このマシン上の多数のメモリコントローラー(16)と関係があると思われます。

Performance (10000x 4MB block memcpy):

 1 thread :  1826 MB/sec
 2 threads:  3118 MB/sec
 3 threads:  4121 MB/sec
 4 threads: 10020 MB/sec
 5 threads: 12848 MB/sec
 6 threads: 14340 MB/sec
 8 threads: 17892 MB/sec
10 threads: 21781 MB/sec
12 threads: 25721 MB/sec
14 threads: 25318 MB/sec
16 threads: 19965 MB/sec
24 threads: 13158 MB/sec
32 threads: 12497 MB/sec

3と4のスレッドの間の大きなパフォーマンスジャンプがわかりません。このようなジャンプの原因は何ですか?

この同じ問題に遭遇する可能性のある他の人のために以下に書いたMemcpyコードを含めました。このコードにエラーチェックがないことに注意してください。これをアプリケーションに追加する必要がある場合があります。

#define NUM_CPY_THREADS 4

HANDLE hCopyThreads[NUM_CPY_THREADS] = {0};
HANDLE hCopyStartSemaphores[NUM_CPY_THREADS] = {0};
HANDLE hCopyStopSemaphores[NUM_CPY_THREADS] = {0};
typedef struct
{
    int ct;
    void * src, * dest;
    size_t size;
} mt_cpy_t;

mt_cpy_t mtParamters[NUM_CPY_THREADS] = {0};

DWORD WINAPI thread_copy_proc(LPVOID param)
{
    mt_cpy_t * p = (mt_cpy_t * ) param;

    while(1)
    {
        WaitForSingleObject(hCopyStartSemaphores[p->ct], INFINITE);
        memcpy(p->dest, p->src, p->size);
        ReleaseSemaphore(hCopyStopSemaphores[p->ct], 1, NULL);
    }

    return 0;
}

int startCopyThreads()
{
    for(int ctr = 0; ctr < NUM_CPY_THREADS; ctr++)
    {
        hCopyStartSemaphores[ctr] = CreateSemaphore(NULL, 0, 1, NULL);
        hCopyStopSemaphores[ctr] = CreateSemaphore(NULL, 0, 1, NULL);
        mtParamters[ctr].ct = ctr;
        hCopyThreads[ctr] = CreateThread(0, 0, thread_copy_proc, &mtParamters[ctr], 0, NULL); 
    }

    return 0;
}

void * mt_memcpy(void * dest, void * src, size_t bytes)
{
    //set up parameters
    for(int ctr = 0; ctr < NUM_CPY_THREADS; ctr++)
    {
        mtParamters[ctr].dest = (char *) dest + ctr * bytes / NUM_CPY_THREADS;
        mtParamters[ctr].src = (char *) src + ctr * bytes / NUM_CPY_THREADS;
        mtParamters[ctr].size = (ctr + 1) * bytes / NUM_CPY_THREADS - ctr * bytes / NUM_CPY_THREADS;
    }

    //release semaphores to start computation
    for(int ctr = 0; ctr < NUM_CPY_THREADS; ctr++)
        ReleaseSemaphore(hCopyStartSemaphores[ctr], 1, NULL);

    //wait for all threads to finish
    WaitForMultipleObjects(NUM_CPY_THREADS, hCopyStopSemaphores, TRUE, INFINITE);

    return dest;
}

int stopCopyThreads()
{
    for(int ctr = 0; ctr < NUM_CPY_THREADS; ctr++)
    {
        TerminateThread(hCopyThreads[ctr], 0);
        CloseHandle(hCopyStartSemaphores[ctr]);
        CloseHandle(hCopyStopSemaphores[ctr]);
    }
    return 0;
}

他のヒント

くなった場合での作業は実行時にまだコンパイル時にございますのでご注意下さいてSSEまたは類似の拡張を可能としてのベクトルユニットがあまり書け128ビットのメモリと比べて64ビットのCPUを搭載しています。

この実装.

んになっていることを確認し ソースとデスティネーションが揃って128ビット.ご送元と転送先は揃ってそれぞれ互いにごmemcpy()をいくつかの深刻なマジックがあります。:)

必要なメモリパフォーマンスを取得するには、いくつかの障壁があります。

  1. 帯域幅 - データがメモリからCPUに速く移動し、再び戻ることができる速さには制限があります。によると このウィキペディアの記事, 、266MHz DDR3 RAMの上限は約17GB/sです。ここで、MEMCPYを使用すると、データが読み取られてから書かれているため、最大転送速度を取得するためにこれを半分にする必要があります。ベンチマークの結果から、システムで可能な限り速いRAMを実行していないようです。余裕がある場合は、マザーボード / RAMをアップグレードします(そして、それは安くはありません、英国のオーバークロッカーは現在3x4GB PC16000を£400で持っています)

  2. OS -Windowsは先制的なマルチタスクOSであるため、他のプロセスを調べて物事を行うようにプロセスが停止されることがよくあります。これにより、キャッシュが鳴り響き、転送が失速します。最悪の場合、プロセス全体がディスクにキャッシュされる可能性があります!

  3. CPU-移動中のデータには、RAM-> L2キャッシュ - > L1キャッシュ - > CPU-> L1-> L2-> RAM。 L3キャッシュさえあるかもしれません。 CPUを巻き込みたい場合は、L1をコピーしている間、本当にL2をロードしたいと思います。残念ながら、最新のCPUは、L1をロードするのにかかった時間よりも速くL1キャッシュブロックを通過することができます。 CPUには、CPUにストリーミングデータを順番にストリーミングするが、まだ問題が発生する場合に大いに役立つメモリコントローラーがあります。

もちろん、何かをするためのより速い方法は、それをしないことです。キャプチャされたデータをRAMのどこにでも書くか、固定場所で使用されるバッファーであることができますか。どこにでも書くことができれば、Memcpyはまったく必要ありません。それが修正されている場合、データを所定の位置に処理して、ダブルバッファ型システムを使用できますか?つまり、データのキャプチャを開始し、半分いっぱいになったら、データの前半の処理を開始します。バッファがいっぱいになったら、キャプチャされたデータの書き込みを開始して、後半を処理します。これには、アルゴリズムがキャプチャカードが生成するよりも速くデータを処理できる必要があります。また、処理後にデータが破棄されると想定しています。事実上、これはコピープロセスの一部として変換されたMEMCPYであるため、次のことを示しています。

load -> transform -> save
\--/                 \--/
 capture card        RAM
   buffer

それ以外の:

load -> save -> load -> transform -> save
\-----------/
memcpy from
capture card
buffer to RAM

または、より速いラムを取得してください!

編集:別のオプションは、データソースとPC間のデータを処理することです。DSP / FPGAをそこに置くことはできますか?カスタムハードウェアは、汎用CPUよりも常に高速になります。

別の考え:高性能グラフィックスのことを行ってからしばらく経ちましたが、グラフィックカードにデータをDMAしてから再びDMAできますか? Cudaを利用して処理の一部を行うこともできます。これにより、CPUはメモリ転送ループから完全に取り除かれます。

注意すべきことの1つは、あなたのプロセス(したがってのパフォーマンスが memcpy())タスクのOSスケジューリングの影響を受けます - これがあなたのタイミングにどれだけの要因であるかを言うのは難しいです、BU TITは制御が困難です。デバイスDMA操作は、CPUがキックオフされた後に実行されていないため、これの対象ではありません。ただし、アプリケーションは実際のリアルタイムアプリケーションであるため、まだお客様がいない場合は、Windowsのプロセス/スレッドの優先度設定を実験することをお勧めします。他のプロセス(およびマシンでのユーザーエクスペリエンス)で本当にマイナスの影響を与える可能性があるため、これに注意する必要があることに注意してください。

心に留めておくべきもう1つのことは、OSメモリ仮想化がここで影響を与える可能性があるということです。コピーしているメモリページが実際に物理的なRAMページにバックされていない場合、 memcpy() 操作は、OSの障害になり、その物理的なバッキングを所定の位置に取得します。 DMAページは物理メモリにロックされる可能性が高い(DMA操作のためである必要があるため)、ソースメモリは memcpy() この点では問題ではない可能性があります。 Win32の使用を検討する場合があります VirtualAlloc() 宛先メモリを確認するAPI memcpy() コミットしていると思います VirtualAlloc() これは適切なAPIですが、私が忘れているより良いAPIがあるかもしれません - このようなことをする必要があるのでしばらく経ちました)。

最後に、使用できるかどうかを確認してください Skizzが説明したテクニック 避けるため memcpy() 全体的に - リソースが許可されている場合は、それが最善の策です。

まず、メモリが16バイトの境界に並べられていることを確認する必要があります。そうしないと、ペナルティが取得されます。これは最も重要なことです。

標準に準拠したソリューションが必要ない場合は、コンパイラ固有の拡張機能を使用して物事が改善されるかどうかを確認できます。 memcpy64 (利用可能なものがある場合は、コンパイラドキュメントに確認してください)。事実はそれです memcpy単一のバイトコピーを処理できる必要がありますが、この制限がない場合、一度に4バイトまたは8バイトを移動すると、はるかに高速です。

繰り返しますが、インラインアセンブリコードを作成するオプションですか?

おそらく、より大きなメモリ領域をどのように処理するかについてもう少し説明できますか?

アプリケーション内で、バッファーをコピーするのではなく、単にバッファの所有権を単純に渡すことが可能でしょうか?これにより、問題が完全になくなります。

または使用しています memcpy 単にコピーするだけではありませんか?おそらく、メモリのより大きな領域を使用して、キャプチャしたものからデータの連続ストリームを構築しますか?特に、一度に1つの文字を処理する場合は、途中で会うことができる場合があります。たとえば、「連続メモリ領域」ではなく、「バッファーの配列」として表されるストリームに対応するために、処理コードを適応させることができる場合があります。

SSE2レジスタを使用して、MEMCPYのより良い実装を作成できます。 VC2010のバージョンはすでにこれを行っています。したがって、あなたがそれを整列させたメモリを渡すならば、質問はもっとです。

たぶん、あなたはVC 2010のバージョンよりも上手くできますが、それを行う方法についての理解が必要です。

PS:コピーを完全に防ぐために、倒立コールでバッファーをユーザーモードプログラムに渡すことができます。

私が読むことをお勧めするソースの1つはmplayer'sです fast_memcpy 働き。また、予想される使用パターンを検討し、最新のCPUには、作成しているデータを読み返す必要があるかどうかをCPUに通知できる特別なストアの指示があることに注意してください。データを読み返さないことを示す指示を使用する(したがって、キャッシュする必要はない)ことは大規模な勝利になる可能性があります memcpy オペレーション。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top