реклама на сайте
подробности

 
 
> О производительности memcpy, Навеяно темой "jpeg на at91sam9g20"
aaarrr
сообщение Aug 22 2010, 04:30
Сообщение #1


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(AlexandrY @ Aug 22 2010, 01:41) *
Надо будет проверить влияние этого на производительность.

Проверил интереса для. Должен сказать, что результаты вышли довольно любопытные, поэтому решил изложить их здесь.

В ходе эксперимента измерялась скорость копирования 256 блоков по одному мегабайту с различными смещениями источника и приемника данных. В качестве тестовой платформы использовался процессор EP9312 (ARM920T, FCLK = 200MHz, BCLK = 100MHz).
Первое число в квадратных скобках - смещение приемника, второе - источника, скорость приводится в мегабайтах в секунду:
Код
copy[0][0]: 60.674
copy[1][0]: 45.506
copy[2][0]: 45.506
copy[3][0]: 45.506

copy[0][1]: 46.397
copy[1][1]: 55.246
copy[2][1]: 45.506
copy[3][1]: 45.506

copy[0][2]: 46.397
copy[1][2]: 46.397
copy[2][2]: 55.245
copy[3][2]: 45.506

copy[0][3]: 46.397
copy[1][3]: 46.397
copy[2][3]: 46.397
copy[3][3]: 55.245

Примечательны два обстоятельства:
1. Относительно небольшое против ожидаемого снижение скорости при работе с неодинаковыми смещениями у источника и приемника
2. Заметное падение скорости в условиях, казалось бы близких к идеальным (n, n; при n > 0).

Объяснение первому эффекту находится достаточно просто: 60 МБайт/с - это ограничение, накладываемое производительностью контроллера памяти. И если снизить частоту ядра, оставив частоту шины на том же уровне (FCLK = BCLK = 100MHz), то разница в результатах получается более значительной:
Код
copy[0][0]: 60.672
copy[1][0]: 32.233
copy[2][0]: 32.233
copy[3][0]: 32.233

copy[0][1]: 32.233
copy[1][1]: 53.933
copy[2][1]: 32.233
copy[3][1]: 32.233
...


Второй эффект куда любопытнее. Доподлинно установить его происхождение не удалось. Можно только определенно сказать, что он не связан напрямую с работой кэша и буфера записи. Наиболее логичным представляется предположение, что смещение данных на слово ломает где-то burst-передачи, создавая тем самым некоторый оверхед. Скорее всего это особенность данного конкретного процессора и его контроллера памяти.

Зависимость скорости копирования от смещения данных, число в квадратных скобках - смещение в 32-битных словах:
Код
copy[0w]: 60.674102
copy[1w]: 55.245381
copy[2w]: 55.226639
copy[3w]: 52.582269
copy[4w]: 60.672306
copy[5w]: 55.246603
copy[6w]: 55.211088
copy[7w]: 52.584118
copy[8w]: 60.674150
...


И, наконец, последняя табличка. Она показывает скорость копирования при расположении источника и приемника данных в разных банках SDRAM.
Код
copy[0w]: 91.010796
copy[1w]: 79.629958
copy[2w]: 79.629988
copy[3w]: 73.539810
copy[4w]: 91.006777
copy[5w]: 79.630088
copy[6w]: 79.630112
copy[7w]: 73.539468
copy[8w]: 91.011220
...



Если резюмировать, то для эффективного копирования надо:
1. Выравнивать данные, причем иногда бывает мало и границы слова. Логичным представляется выравнивание по границе строки кэша.
2. По возможности располагать источник и приемник в разных банках SDRAM. Это позволит контроллеру памяти дольше держать банки открытыми (т.е. сэкономить на precharge).
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
AlexandrY
сообщение Aug 22 2010, 11:58
Сообщение #2


Ally
******

Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050



Тут важнее каким компилером это делалось и какая шина была к SDRAM.

С другой стороны в драйверах где скорость особенно важна практически нет свободы выбора как будут размещены приходящие в драйвер данные.
Зато есть свобода выбора компилятора wink.gif
Go to the top of the page
 
+Quote Post
VslavX
сообщение Aug 22 2010, 12:39
Сообщение #3


embarrassed systems engineer
*****

Группа: Свой
Сообщений: 1 083
Регистрация: 24-10-05
Из: Осокорки
Пользователь №: 10 038



Цитата(AlexandrY @ Aug 22 2010, 14:58) *
Тут важнее каким компилером это делалось и какая шина была к SDRAM.

Я бы сказал что не от компилятора, а от конкретной реализации memcpy() - она обычно на ассемблере пишется, компилятор никак не результат повлиять не может.

aaarrr
Вы не могли бы добавить архивчик с исходником использованной в Ваших тестах memcpy()? Для полноты картины smile.gif
Про разные банки - эффект известный, Ваш тест очень хорошо его отобразил. Только пользоваться этим эффектом трудно, у тех контроллеров что мне попадались, биты адреса банка замешивались в середину физического адреса. И получалось что в адресном пространстве подряд шли страницы из разных банков, да еще они могли на разных платах быть разного размера - в зависимости от того какие чипы запаяли. В среднем (когда не задумываться о банках) это наверное эффективней, тем более в DDR2 банков уже может быть 8. Но при этом сознательно "разбанковать" данные становится трудновато.
Go to the top of the page
 
+Quote Post
AlexandrY
сообщение Aug 22 2010, 13:30
Сообщение #4


Ally
******

Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050



Цитата(VslavX @ Aug 22 2010, 15:39) *
Я бы сказал что не от компилятора, а от конкретной реализации memcpy() - она обычно на ассемблере пишется, компилятор никак не результат повлиять не может.


Почему вы решили что все пишут на GCC?
Ни в IAR, ни в Keil, ни в GHS, ни в RealView исходники memcpy недоступны.
Никто бы не стал тестировать memcpy если б были исходники этой функции.
Go to the top of the page
 
+Quote Post
VslavX
сообщение Aug 22 2010, 14:02
Сообщение #5


embarrassed systems engineer
*****

Группа: Свой
Сообщений: 1 083
Регистрация: 24-10-05
Из: Осокорки
Пользователь №: 10 038



Цитата(AlexandrY @ Aug 22 2010, 16:30) *
Почему вы решили что все пишут на GCC?
Ни в IAR,

Надо признать, резон в замечании есть. Тут дело не столько в GCC, сколько в том, что memcpy() лично для меня функция важная и тщательно изученная не для одной архитектуры. Вот я и решил что все ее код "знают в лицо". Был неправ, что сказать . BTW, для IAR Full версии исходники доступны, и это они зря - там грустно sad.gif

Цитата(AlexandrY @ Aug 22 2010, 16:30) *
Никто бы не стал тестировать memcpy если б были исходники этой функции.

Исходники (ессно, в нормальном варианте) там бывают довольно любопытные - иногда я много нового узнаю smile.gif. ИМХО, тестировать memcpy() следует потому что на современных системах производительность зависит не только (далеко не только) от того что написано в исходнике этой функции. Лично я всегда при помощи тестов memcpy() (своей имплементации RTL) тюнингую настройки контроллера DRAM, арбитра шины и прочие подобные мелочи.


Цитата(zltigo @ Aug 22 2010, 16:48) *
ибо в IAR 4x был мрак с memcpy(), но IAR - для 4x и 5.x день и ночь по результатам.

Э-э-э-э, то есть 5-ый еще хуже чем 4-ый? Да куда ж там дальше-то. Я последний раз IAR-овские исходники смотрел в 4.30 - правда мрак.
Go to the top of the page
 
+Quote Post
zltigo
сообщение Aug 22 2010, 14:13
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



QUOTE (VslavX @ Aug 22 2010, 16:02) *
IAR Full версии исходники доступны, и это они зря - там грустно sad.gif

Э,то что Вам показалось исходниками, это не они, это какая-то заглушка. Исходников нет и в полной.
QUOTE
Э-э-э-э, то есть 5-ый еще хуже чем 4-ый? Да куда ж там дальше-то.

Вообще-то я хотел сказать, что совсем наоборот, и вроде так и сказал?
Вот, то, что использовал вместо штатного memcpy() в 4x IAR:
CODE
//---------------------------------------------------------------------------
// Fast memcpy() & memove() For IAR ARM ANSI C/C++ Compiler
//---------------------------------------------------------------------------

NAME memcpy

RSEG CSTACK:DATA:NOROOT(2)

MULTWEAK ??memcpy??rT
MULTWEAK ??memmove??rT
PUBLIC memcpy
PUBLIC memmove

memcpy SYMBOL "memcpy"
??memcpy??rT SYMBOL "??rT", memcpy

memmove SYMBOL "memmove"
??memmove??rT SYMBOL "??rT", memmove


RSEG CODE:CODE:NOROOT(2)
THUMB
??memcpy??rT:
BX PC
Nop
REQUIRE memcpy


RSEG CODE:CODE:NOROOT(2)
ARM

#ifndef configUSE_MEMCPY8W
#define WORDS8_TRANSFER 0
#else
#if configUSE_MEMCPY8W == 1
#define WORDS8_TRANSFER 1
#else
#define WORDS8_TRANSFER 0
#endif
#endif

//---------------------------------------------------------------------------
// void *(memcpy)(void *p1, const void *p2, size_t n)
// Copy char p2[n] to p1[n]
//---------------------------------------------------------------------------
memcpy:
teq r2,#0 // Is p1 == 0 ?
bxeq lr // If p1 == 0, return

stmdb sp!,{lr} // Push return address
mov r12,r0 // Copy pointer p1
cmp r2,#8 // Is buffer long or short?
ble byteserial // Jump if n <= 8

sub r3,r0,r1 // Compare pointers p1, p2
tst r3,#3 // Strings aligned same?
bne byteserial // Jump if buffers not aligned


// Both strings are similarly aligned WRT word boundaries.
// At least a portion of the data can be copied an entire
// word at a time, which is faster than copying bytes.
wordserial:
ands r3,r0,#3 // Check byte alignment
beq wordaligned // Jump if p1, p2 word-aligned

rsb r3,r3,#4 // m = no. of odd initial bytes
sub r2,r2,r3 // n = n - m


// If the two buffers do not begin on word boundaries, begin
// by copying the odd bytes that precede the first full word.
preloop:
ldrb lr,[r1],#1 // Read byte from source
subs r3,r3,#1 // --m (decrement loop count)
strb lr,[r12],#1 // Write byte to destination
bne preloop // Loop if more bytes to move


wordaligned:
#if WORDS8_TRANSFER == 1
movs r3,r2,asr #5 // Any chunks of 8 words?
beq octsdone // Jump if no 8-word chunks

and r2,r2,#0x1F // Subtract chunks from n
stmdb sp!,{r4-r10} // Save registers on stack


// The strings are long enough that we can transfer at least
// some portion of the data in 8-word chunks.
octloop:
ldmia r1!,{r4-r10,lr} // Load 8 words from source
subs r3,r3,#1 // More 8-word chunks to move?
stmia r12!,{r4-r10,lr} // Write 8 words to destination
bne octloop // Loop if more chunks

ldmia sp!,{r4-r10} // Restore registers from stack

octsdone:
#endif
movs r3,r2,asr #2 // Any more whole words to move?
beq wordsdone // Jump if no more whole words


// Copy as much of the remaining data as possible one word at
// a time.
wordloop2:
ldr lr,[r1],#4 // Read next word from source
subs r3,r3,#1 // Decrement word count
str lr,[r12],#4 // Write next word to destination
bne wordloop2 // Loop while more words to move

wordsdone:
ands r2,r2,#3 // Any last bytes to transfer?
beq theend // Return if already done


// The two strings do not end on word boundaries.
// Copy the remaining data one byte at a time.
byteserial:
ldrb lr,[r1],#1 // Read byte from source
subs r2,r2,#1 // --n (decrement loop count)
strb lr,[r12],#1 // Write byte to destination
bne byteserial // Loop if more bytes to move

theend:
ldmia sp!,{lr} // Return
bx lr

//---------------------------------------------------------------------------
RSEG CODE:CODE:NOROOT(2)
THUMB
??memmove??rT:
BX PC
Nop
REQUIRE memmove


RSEG CODE:CODE:NOROOT(2)
ARM

//---------------------------------------------------------------------------
// Safely copy c bytes from source s to destination d.
// void *memmove(void *d, const void *s, unsigned c);
//---------------------------------------------------------------------------
memmove:
cmp r0,r1 // Is d > s ?
bls memcpy // Jump to memcpy if d <= s

// Need to copy backwards, starting at tail ends of source and
// destination arrays. Copy a word or a byte at a time?
orr r3,r1,r0 // tmp = s | d
orr r3,r3,r2 // tmp = s | d | c
ands r3,r3,#3 // Is tmp even multiple of 4?

add r1,r1,r2 // s + c (end of source buffer)
add r2,r2,r0 // d + c (end of dest'n buffer)
beq move1 // Jump if tmp is multiple of 4
b move2

// Because the source and destination arrays are not aligned to even
// word boundaries in memory, transfer only a byte at a time.
move3:
ldrb r3,[r1,#-1]! // Load next byte from source
strb r3,[r2,#-1]! // Store next byte to dest'n
move2:
teq r0,r2 // More bytes to move?
bne move3 // Jump if more bytes
bx lr // All done

// Because count c is an even multiple of 4 and the source
// and destination arrays begin on even word boundaries, move
// an entire word at a time from source to destination.
move4:
ldr r3,[r1,#-4]! // Load next word from source
str r3,[r2,#-4]! // Store next word to dest'n
move1:
teq r0,r2 // More words to move?
bne move4 // Jump if more words

bx lr // All done

END

Есть еще один сишный memcpy(). Интересно?


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post

Сообщений в этой теме
- aaarrr   О производительности memcpy   Aug 22 2010, 04:30
|- - zltigo   QUOTE (AlexandrY @ Aug 22 2010, 15:30) Ни...   Aug 22 2010, 13:48
|- - VslavX   Цитата(zltigo @ Aug 22 2010, 17:13) Э,то ...   Aug 22 2010, 14:53
- - aaarrr   Цитата(VslavX @ Aug 22 2010, 16:39) aaarr...   Aug 22 2010, 15:05
|- - AlexandrY   Цитата(aaarrr @ Aug 22 2010, 18:05) Пожал...   Aug 22 2010, 15:18
|- - aaarrr   Цитата(AlexandrY @ Aug 22 2010, 19:18) По...   Aug 22 2010, 15:47
- - igorsk   RE: О производительности memcpy   Aug 22 2010, 18:47
- - sergeeff   Самый шустрый memcpy для всех случаев выравненных/...   Aug 22 2010, 19:08
|- - aaarrr   RE: О производительности memcpy   Aug 22 2010, 20:32
- - sergeeff   ЦитатаНо данные все равно лучше выравнивать. Это ...   Aug 23 2010, 06:16
|- - MrYuran   Цитата(sergeeff @ Aug 23 2010, 10:16) Это...   Aug 23 2010, 06:25
|- - AlexandrY   Цитата(sergeeff @ Aug 23 2010, 09:16) Это...   Aug 23 2010, 06:45
- - sergeeff   х86 всю свою "жизнь" аппаратно это тоже ...   Aug 23 2010, 07:13
- - AlexandrY   Цитата(sergeeff @ Aug 23 2010, 10:13) х86...   Aug 23 2010, 07:50
- - MrYuran   Цитата(AlexandrY @ Aug 23 2010, 11:50) Ес...   Aug 23 2010, 08:07


Reply to this topicStart new topic
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 23rd July 2025 - 22:29
Рейтинг@Mail.ru


Страница сгенерированна за 0.01443 секунд с 7
ELECTRONIX ©2004-2016