Как увеличить производительность memcpy

Резюме:

memcpy, похоже, не может передавать более 2 ГБ / сек в моей системе в реальном или тестовом приложении. Что я могу сделать, чтобы ускорить копирование из памяти в память?

Полная информация:

Как часть приложения для сбора данных (с использованием некоторого специализированного оборудования), мне нужно скопировать около 3 ГБ / сек из временных буферов в основная память. Для сбора данных я предоставляю драйверу оборудования ряд буферов (по 2 МБ каждый). Аппаратный DMA передает данные в каждый буфер, а затем уведомляет мою программу, когда каждый буфер заполнен. Моя программа очищает буфер (memcpy до другого, большего блока ОЗУ) и повторно помещает обработанный буфер на карту для повторного заполнения. У меня проблемы с тем, что memcpy перемещает данные достаточно быстро. Кажется, что копирование из памяти в память должно быть достаточно быстрым, чтобы поддерживать 3 ГБ / сек на оборудовании, на котором я работаю. Lavalys EVEREST дает мне результат теста копирования памяти 9337 МБ / с, но я не могу приблизиться к этим скоростям с помощью memcpy, даже в простой тестовой программе.

Я изолировал проблему производительности, добавив / удалив вызов memcpy внутри кода обработки буфера. Без memcpy я могу работать с полной скоростью передачи данных - около 3 ГБ / сек. При включенном memcpy я ограничен скоростью около 550 МБ / с (при использовании текущего компилятора).

Чтобы протестировать memcpy в моей системе, я написал отдельную тестовую программу, которая просто вызывает memcpy для некоторых блоков данных. (Я разместил код ниже) Я запускал это как в компиляторе / IDE, который я использую (National Instruments CVI), так и в Visual Studio 2010. Хотя в настоящее время я не использую Visual Studio, я готов произвести переключение, если это обеспечит необходимую производительность. Однако, прежде чем слепо двигаться дальше, я хотел убедиться, что он решит мои проблемы с производительностью memcpy.

Visual C ++ 2010: 1900 МБ / с

NI CVI 2009: 550 МБ / с

Хотя я не удивлен, что CVI значительно медленнее, чем Visual Studio, я удивлен, что производительность memcpy настолько низкая. Хотя я не уверен, что это напрямую сопоставимо, это намного ниже, чем пропускная способность теста EVEREST. Хотя мне и не нужен такой уровень производительности, необходим минимум 3 ГБ / сек. Конечно, реализация стандартной библиотеки не может быть намного хуже, чем то, что использует EVEREST! ​​

Что я могу сделать, если что-нибудь, чтобы сделать memcpy быстрее в этой ситуации?


Детали оборудования: Хотя мне и не нужен такой уровень производительности, необходим минимум 3 ГБ / сек. Конечно, реализация стандартной библиотеки не может быть намного хуже, чем то, что использует EVEREST! ​​

Что я могу сделать, если что-нибудь, чтобы сделать memcpy быстрее в этой ситуации?


Детали оборудования: Хотя мне и не нужен такой уровень производительности, необходим минимум 3 ГБ / сек. Конечно, реализация стандартной библиотеки не может быть намного хуже, чем то, что использует EVEREST! ​​

Что я могу сделать, если что-нибудь, чтобы сделать memcpy быстрее в этой ситуации?


Детали оборудования: AMD Magny Cours - 4-кратное восьмеричное ядро 128 ГБ DDR3 Windows Server 2003 Enterprise 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;
}

РЕДАКТИРОВАТЬ: Если у вас есть дополнительные пять минут и вы хотите внести свой вклад, можете ли вы запустить приведенный выше код на своем компьютере и опубликовать свое время в качестве комментария?

48
задан leecbaker 23 November 2010 в 21:22
поделиться