LINUX.ORG.RU

Как быстрее всего читать память(RAM) в Linux.


0

4

Здравствуйте. Собственно имеется ARM железка на SoC Cyclone V от Altera с Linux на борту. Подскажите пожалуйста как быстрее всего читать RAM. memcpy? Или лучше написать модуль ядра? Блоками по 4 байта или больше? С Linux новичек, пользуюсь недавно. Хотелось бы выжать максимум скорости на чтение. Пробовал с помощью dd тестировать скорость памяти, максимум что получал 200-300 МБ/с, при памяти 400(800)МГц и 32 битной шине. Если не ошибаюсь расчетная скорость должна быть ~3 ГБ/с. Как приблизиться к ней?

Что значит «читать RAM»? Какая вообще стоит задача?

Про memcpy: в нём уже встроено блочное копирование.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от beastie

Читать данные из оперативной памяти и обрабатывать. В DDR3 память будет записывать ПЛИС находящаяся в чипе, процессор должен успеть обработать данные, для обработки ненужно много ресурсов. Я пробовал читать память по 4 байта и c помощью memcpy большими блоками(чтение памяти в переменную внутри цикла, обращался к памяти через /dev/mem), скорость намного меньше расчетной, даже половины той скорости нет.

Pro100MoNEy
() автор топика
Ответ на: комментарий от beastie

ПЛИС будет складывать в память 128 бит в секунду с максимальной скоростью 350МГц.

Pro100MoNEy
() автор топика

Выделяй память с выравниванием, читай, например, постранично (или кратно размеру страниц). Остерегайся кэш-миссов.

yoghurt ★★★★★
()

Какой кросскомпилятор используете ? Попробуйте готовую сборку Linaro - они собирают c оптимизацией для armv7, в glibc есть оптимизированный вариант memcpy под neon и vfp, например в бесплатном codesorcery lite библиотеки только для armv5. Еще можно усомниться в правильности настройки контроллера внешней памяти. Копировать блоками будет быстрей, вообще 200-300 МБ/с - скорость похожа на доступ к некэшируемым регионам памяти DMA.

anonymous
()

Ну для начала проверь, что у тебя governor для cpu не powersave (cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor), а то там DVFS обычно снижает и скорость памяти, и вообще с уменьшением частоты сначала скорость доступа к памяти падает почти линейно, а потом резко чуть ли не экспоненциально.

Непонятно, зачем куда-то копировать эту память. Для того, чтобы исключить перезапись данных, пока обрабатываем, можно разбить всю видимую с FPGA память на N блоков, например, по 64 мегабайта и завести регистр с флажками, какой из буферов принадлежит FPGA, а какой процессору. FPGA записала - сняла флажок, CPU обработал - поставил обратно.

Чтобы можно было mmap'нуть (и даже ioremap'нуть из ядра) память - она должна в ядре принадлежать какому-то struct device - поэтому без создания класса девайса это просто так не сделать, да и потом замучаешься писать обработчик для mmap в struct file_operations. То есть не так. Ты можешь mmap'нуть кусок памяти через /dev/mem, но получишь виртуальный адрес, из которого не сможешь получить обратно физический (нужный FPGA), а также не сможешь гарантировать, что он отражён в физически непрерывную область памяти (я не знаю, умеет ли твоя FPGA в таком случае scatter-gather или есть ли там iommu, но в любом случае это долго писать).

Поэтому тупо в рамках недели вредных советов. Так как доступ в RAM со стороны ПЛИСа «почти» когерентный (почти - потому что нельзя делать LL/SC http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407e/CACGGBCF... - то есть, не стоит пытаться реализовать спинлок в такой памяти) - можно из FPGA писать сразу в RAM.

Смотришь, сколько у тебя RAM и по какому адресу (в DTS файле) и пишешь ядру параметр mem=700M, например. Вуаля - ядро добавило в pagetable всю память, а юзает только 700 мегабайт. И тут мы такие из юзерспейса делаем mmap(адрес + (700 << 20)) на /dev/mem - и можно работать с памятью без memcpy.

Когда получил указатель от mmap - его же можно пихать куда угодно - хоть в write, хоть в splice - в лучшем случае у тебя будет вообще одно копирование - из памяти в устройство (контроллер карты памяти или сетевой карты), да и тот с DMA.

anonymous
()
Ответ на: комментарий от anonymous

Чтобы можно было ... (и даже ioremap'нуть из ядра) память - она должна в ядре принадлежать какому-то struct device

фигня какая-то - зачем ioremap-у какой-то struct device ? ioremap отображает физические адреса в виртуальные адреса kernel space, он как раз добавляет страницы физической памяти в page table.

пишешь ядру параметр mem=700M, например. Вуаля - ядро добавило в pagetable всю память, а юзает только 700 мегабайт.

все что выше mem никуда не добавляется, для этого он и нужен

anonymous
()
Ответ на: комментарий от beastie

Из того, что я могу по-быстрому нагуглить, достигнутые тобой 300MBps уже неплохой результат.

Кстати да, я попутал копирование с чтением - давно не мерял ничего :) более-менее нормальные показатели. Вот на моем cortex-a9 и как раз DDR3 на частоте 400M, 32-битное подключение

# bw_mem 100M bcopy
100.00 372.75
# bw_mem 100M rd
100.00 822.88
# cat /proc/cpuinfo 
processor	: 0
model name	: ARMv7 Processor rev 10 (v7l)
BogoMIPS	: 790.52
Features	: swp half thumb fastmult vfp edsp neon vfpv3 tls 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x2
CPU part	: 0xc09
CPU revision	: 10

вот еще результаты для сравнения

http://www.pengutronix.de/development/kernel/arm-benchmarks-20100729_en.html

anonymous
()
Ответ на: комментарий от anonymous

Linaro и использую. Но судя по постам выше скорость может быть больше. А контроллер внешней памяти настраивается в ОС или в прошивке железки?

Pro100MoNEy
() автор топика
Ответ на: комментарий от anonymous

Спасибо за совет осталось разобраться в функциях и терминах. Копировать не нужно FPGA будет писать в память данные сама(как я понимаю процессор и ОС не будут участвовать в этом, это нужно уточнить этой частью занимаюсь не я, может быть FPGA будет выставлять прерывание или записывать в определенное место количество записанных данных, это пока не обсуждалось), процессор должен успеть просматривать память в реальном времени(проверять условие). DTS дерево устройств правильно? Параметр ядру при конфигурировании задавать?

Pro100MoNEy
() автор топика
Ответ на: комментарий от anonymous

Какой кросскомпилятор используете ?

Какой-то хамоватый анонимус на ЛОРе пошёл, на вы почему-то обращается...

UVV ★★★★★
()
Ответ на: комментарий от anonymous

фигня какая-то - зачем ioremap-у какой-то struct device ? ioremap отображает физические адреса в виртуальные адреса kernel space, он как раз добавляет страницы физической памяти в page table.

Действительно, я попутал с devm_ioremap - использовал его, но почему-то отложилось про ioremap.

все что выше mem никуда не добавляется, для этого он и нужен

А это поведение описано в документации? Просто вот тут чувак рядом сидит - он так сделал, у него работает. И в интернете в паре мест тоже видел, что так делают. Понятно, что костыль. Я бегло посмотрел код - мне кажется, дело в том, что там вроде как две сущности - memory bank, который ограничивает доступную ядру виртуальную память, и memory region, который берётся из DTS, и никак не обрезается, для него создаётся IORESOURCE_MEM, и мапится он целиком.

anonymous
()
Ответ на: комментарий от Pro100MoNEy

Параметр ядру при конфигурировании задавать?

Обычно в конфиге убута. Ну или в DTS в chosen/bootargs прописать, как в zynq-7000

anonymous
()
Ответ на: комментарий от splinter

для начала надо узнать, как ОП замеряет скорость. при чтении последовательных адресов откуда взяться промахам?

anonymous
()
Ответ на: комментарий от anonymous

А это поведение описано в документации? Просто вот тут чувак рядом сидит - он так сделал, у него работает.

Одно другому не противоречит - я же не говорю что не работает, просто заметил непонимание - поправил :) Если в ядре к буферам DMA доступ не нужен - память не обязательно отображать в виртуальном адресном пространстве ядра, драйвер может манипулировать физическими адресами - прописывает например очередной адрес для DMA устройству сам не обращаясь по этим адресам. В юзерспейсе процесс для доступа вызывает mmap и эти регионы отображаются в виртуальной памяти пользовательского процесса. Сейчас наверно уже никто так память для DMA не резервирует - давно уже есть CMA и он прозрачен для DMA-api, драйверу не надо ничего знать о резервировании, он просто делает запрос на выделение памяти из области DMA любого размера, память резервируется на этапе инициализации ядра автоматически.

# dmesg | grep cma
cma: CMA: reserved 256 MiB at 20000000
anonymous
()
Ответ на: комментарий от UVV

зато теперь ты посрал в технической темеместо анона

anonymous
()
Ответ на: комментарий от beastie

В

 /sys/devices/system/cpu/cpu0/ 
нет директории cpufreq. Но в системе есть утилита cpufreq-info Правда оказалось нет драйвера на ЦПУ.
 root@socfpga_cyclone5:~# cpufreq-info 
cpufrequtils 008: cpufreq-info (C) Dominik Brodowski 2004-2009
Report errors and bugs to cpufreq@vger.kernel.org, please.
analyzing CPU 0:
  no or unknown cpufreq driver is active on this CPU
analyzing CPU 1:
  no or unknown cpufreq driver is active on this CPU
Информация о ЦПУ
root@socfpga_cyclone5:~# cpufreq-aperf 
CPU doesn't support APERF/MPERF
root@socfpga_cyclone5:~# cat /proc/cpuinfo 
processor	: 0
model name	: ARMv7 Processor rev 0 (v7l)
BogoMIPS	: 1836.64
Features	: swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls lpae 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x3
CPU part	: 0xc09
CPU revision	: 0

processor	: 1
model name	: ARMv7 Processor rev 0 (v7l)
BogoMIPS	: 1843.20
Features	: swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls lpae 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x3
CPU part	: 0xc09
CPU revision	: 0

Hardware	: Altera SOCFPGA
Revision	: 0000
Serial		: 0000000000000000
Измеряю скорость с помощью lmbench и простой программы использующей /dev/mem

int main( int argc, char *argv[] )
{
    int fd;
    if( argc < 2 ) {
        printf( "Usage:\n" );
        printf( "%s size  [memcpy block size] \n", argv[ 0 ] );
        exit( -1 );
    }

    fd = open( "/dev/mem", O_RDWR | O_SYNC );
    if( fd < 0 ) {
        printf( "open error" );
        exit( -1 );
    }
    void *map_page_addr;

    unsigned long long map_size;
    unsigned long long block_size=1;
    map_size = strtoul( argv[ 1 ], NULL, 0 );
    if(argc>2)
        block_size = strtoul( argv[ 2 ], NULL, 0 );


    map_page_addr = mmap( 0, map_size , PROT_READ , MAP_PRIVATE, fd, 0);
    if( map_page_addr == MAP_FAILED ) {
        printf( "mmap error" );
        exit( -1 );
    }
    register uint32_t i=0;
    register uint32_t max=map_size/4;
    register  uint32_t temp;
    QElapsedTimer t;
    t.start();
    while(i<max){
        temp =*( ( uint32_t *) map_page_addr+i );
        ++i;
    }
    uint32_t readTimeMlSec=t.elapsed();
    temp+=1;
    uint32_t g=temp;
    printf( "Read  %llu bytes, time  = %u miliSec \n",map_size,readTimeMlSec);
    double dmap_size=map_size;
    double dreadTimeMlSec=readTimeMlSec;
    double result=(dmap_size/1024/1024)/(dreadTimeMlSec/1000);
    printf( "%lf MBps\n",result);

    char* ptr= (char*)malloc(map_size);
    i=0;
    t.start();
    while(i<map_size){
        memcpy(ptr,map_page_addr,block_size);
        i+=block_size;
    }
    readTimeMlSec=t.elapsed();
    printf( "MCP: Read  %llu bytes, time  = %u miliSec \n",map_size,readTimeMlSec);
    dmap_size=map_size;
    dreadTimeMlSec=readTimeMlSec;
    result=(dmap_size/1024/1024)/(dreadTimeMlSec/1000);
    printf( "%lf MBps\n",result);
    if( munmap( map_page_addr, map_size ) ) {
        printf( "munmap" );
        exit( -1 );
    }
    close( fd );
    return g;
}

Вывод дают такой:

root@socfpga_cyclone5:~/lmbench-3.0-a9/bin/x86_64-linux-gnu# ./bw_mem 104857600 frd
104.86 377.02
root@socfpga_cyclone5:~# ./memTest 104857600 1024                               
Read  104857600 bytes, time  = 804 miliSec                                      
124.378109 MBps                                                                 
MCP: Read  104857600 bytes, time  = 433 miliSec                                 
230.946882 MBps

Если объявить переменные не в регистровой памяти то скорость чтения падает почти в два раза. lmbench выдает 377 МБс при этом копирует по 512 байт за один проход цикла, вроде memcpy должен бы показать скорость примерно такую же, но не дотягивает даже меняя параметр размера копируемой памяти, при этом выгоднее задать цикл в котором memcpy будет копировать память кусками по 4кб или больше, если передать в параметрах всю память скорость падает.

Я скорее всего что-то не понял или не разобрался, потому что просто не могу поверить что скорость чтения микросхемы ddr3 почти равна скорости гигабитной сетки.

Кстати в lmbench есть параметр позволяющий распараллелить тест, что я и сделал имея два ядра. Скорость увеличилась вдвое, но как такое может быть когда шина обмена с ddr3 на 400 МГц, а процессор работает с частотой почти 1ГГц, с такой частотой по логике память должна не успевать подавать данные и скорость обмена была бы максимальной, разве нет?

Даже попробовал на ПК (core2duo 2.9Ггц , ddr2 400Мгц) тест скорости памяти, результат почти 6ГБ/с, при этом память ddr2, хотя шина на ПК конечно не 32 бита, но разница не должна быть такой огромной.

И в догонку подскажите как посмотреть информацию о памяти на плате, в системе нет dmidecode.

Pro100MoNEy
() автор топика
Ответ на: комментарий от Pro100MoNEy

И в догонку подскажите как посмотреть информацию о памяти на плате, в системе нет dmidecode.

А нет возможности посмотреть маркировки памяти и прогуглить даташиты? Возможно я не прав и интересующая память встроена в чип, но судя по фото циклона 5 - там есть отдельные микросхемы памяти.

Deleted
()
Ответ на: комментарий от Pro100MoNEy

в системе нет dmidecode.

есть мнение что dmidecode работает по i2c с еепром которая распаяна на модуле памяти (для традиционных систем). А если у вас просто чипы ddr3 напаяны на плату, то никакой еепром там нет.

yax123 ★★★★★
()

новичек

За новИчков/нОвичков убивал бы. На сколько надо не любить родной язык, чтобы не различать эти ошибки хотя бы по ударению. Ударный суффикс только -ок! А ударная гласная в корне часто ё: чёрный, например

anonymous
()
Ответ на: комментарий от Pro100MoNEy

Это понятно

если бы тебе было понятно - не задавал бы глупых вопросов :)

FPGA пишет в такую же память со скоростью 1200MBs

только _пишет_, а ты спрашиваешь про копирование - если что это чтение + запись, почувствуй разницу. Еще надо смотреть - какие приоритеты на шине AXI, еще у ARM очень много багов связанных с кешем - надо смотреть ревизию, в ядре Linux для них понятное дело есть work-around но все это - потеря производительности.

anonymous
()
Ответ на: комментарий от anonymous

Согласен, и все же производительность может упасть в два раза(на чтение и запись), но не в 3-4. Не подскажете где смотреть на приоритеты AXI?

Pro100MoNEy
() автор топика
Ответ на: комментарий от Pro100MoNEy

AXI шина с подтверждениями. То есть на каждое чтение или запись slave должен выплюнуть ответ. Там есть возможность burst'а, что скорее всего и сделано в FPGA. Как там CPU пишет/читает, хрен его знает. Если и можно сделать, то копай write combined.

PS цуцлонов в глаза не видел, но из общих соображений так должно быть.

ebantrop
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.