|
|
  |
Как повысить скорость работы по сети AT91SAM7X256 |
|
|
|
May 4 2008, 07:36
|
Участник

Группа: Участник
Сообщений: 70
Регистрация: 5-12-06
Пользователь №: 23 146

|
Цитата(defunct @ May 4 2008, 02:12)  А чем очередь принципиально отличается от массива? Это же массив указателей и пара индексов put/get. В любом случае Вы вольны написать свои обёртки очередей. Насколько я смог разобраться когда-то и вспомнить сейчас, во FreeRTOS, работа с очередями организована через усыпление всех активных задач, что, соответственно, сказывалось на быстродействии системы. Поэтому пришлось переделывать. Сейчас посмотрю что изменилось в новой версии операционки и как это всё сделано в интересующем меня примере.
|
|
|
|
|
May 4 2008, 10:45
|

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

|
Цитата(aaarrr @ May 4 2008, 12:06)  Не надо уходить от memcpy! Обойти можно только наложив дополнительные ограничения, например ввиде обязательной выровненности даннных.... А так сложно.. memcpy() не вовсех либах хороши  у 4 IAR - отвратительные, а в 5 резко улучшили, даже отдельно хвастались, что дескать самый быстрый стал.. При испытаниях действительно в некоторых условиях превосходил ранее мной используемый для старых IAR: CODE 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
А 'уходить' по любому надо, но максимально исключая пересылки память-память в пинципе  Цитата(defunct @ May 3 2008, 23:26)  Но для SAM7 это не так. Надо помнить что слабое место у SAM7 это флеш. В ARM режиме размер кода увеличивается в среднем на 30%, а это значит как минимум на 30% больше обращений к медленной флеш. Плюс в SAM7 применена технология ускоренной выборки для Thumb режима (по две Thumb инструкции за одно обращение к флеш). Т.е. при размещении программы во флеш Thumb режим заведомо в выигрыше. 1. К счатью на SAM7 свет клином не сошелся  . 2. Все не так радужно и с Thumb - на моих реальных программах много использующих 32-битовость увеличение размера кода МНОГО меньше, а количество команд которые по любому надо исполнять ЗАМЕТНО больше. Практически вышесказанное Вами в большей степени относится к коду написанному в "восьмибитном" стиле с обильным использованием 8-бит переменных, что изрядно душит ARM (в обоих режимах). Для изначально "правильных" программ Thumb у меня ПРОИГРАЛ. Правда на SAM7 не гонял, а на LPC там, конечно, MAM описанный Вами эффект дополнительно сглаживает, но не слишком, ибо частота повыше а Flash потормознее.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
May 4 2008, 11:06
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(defunct @ May 4 2008, 00:26)  В ARM режиме резонно написать некоторые особо критические функции, и разместить их в RAM. Но надо смотреть каждую конкретную функцию, т.к. например функция копирования приведенная выше и в ARM режиме и в Thumb компилуруется в 16 инструкций. Листинги привожу ниже: ... Вы уж простите, что лезу, но вменяемые компиляторы делают совсем другой код: Код 623 __arm void memcpy_burst( U32 *pDest, U32 *pSrc, U32 size ) 624 { 625 U32 wCnt = (size + 3) >> 2; // words count to be copied \ memcpy_burst: \ 00000000 032082E2 ADD R2,R2,#+3 \ 00000004 2221A0E1 LSR R2,R2,#+2 626 do 627 { 628 *pDest++ = *pSrc++; \ ??memcpy_burst_0: \ 00000008 ........ LDR R3,[R1], #+4 \ 0000000C ........ STR R3,[R0], #+4 629 } while (--wCnt); \ 00000010 012052E2 SUBS R2,R2,#+1 \ 00000014 FBFFFF1A BNE ??memcpy_burst_0 630 } \ 00000018 0EF0A0E1 MOV PC,LR ;; return Код 623 __thumb void memcpy_burst( U32 *pDest, U32 *pSrc, U32 size ) 624 { 625 U32 wCnt = (size + 3) >> 2; // words count to be copied \ memcpy_burst: \ 00000000 D21C ADDS R2,R2,#+3 \ 00000002 9208 LSRS R2,R2,#+2 626 do 627 { 628 *pDest++ = *pSrc++; \ ??memcpy_burst_0: \ 00000004 0B68 LDR R3,[R1, #+0] \ 00000006 0360 STR R3,[R0, #+0] \ 00000008 091D ADDS R1,R1,#+4 \ 0000000A 001D ADDS R0,R0,#+4 629 } while (--wCnt); \ 0000000C 521E SUBS R2,R2,#+1 \ 0000000E F9D1 BNE ??memcpy_burst_0 630 } \ 00000010 7047 BX LR А вообще-то, правильный memcpy должен работать в ARM и использовать STM/LDM (для больших кусков, для маленьких - развернутое копирование с переходом в нужное место) - тогда будет максимальное быстродействие, конечно, для выровненных данных, без этого - никуда. Цитата Кроме того при прочих равных (если код выполнять из RAM), для IP стека ARM режим практически не даст никакого выигрыша, возможно даже проиграет, из-за big-endian формата данных в пакетах и множества byte и half-word полей. Тут надо смотреть, чтобы хватало регистров. Если в тумбе не помещается в 8 регистров, то код резко ухудшается (и по размеру, и по быстродействию). По поводу big-endian - тоже не согласен, переворот байт в арм-режиме занимает меньше, чем в тумбе.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
May 4 2008, 13:01
|

Гуру
     
Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659

|
Цитата(aaarrr @ May 4 2008, 17:06)  Не надо уходить от memcpy! Стандартные библиотеки писаны далеко не дураками, и не на C. Ссылка. Ну тогда объясните, чем memcpy лучше обмена списками вида Код typedef struct __DATALIST { struct __DATALIST *next; /* next datalist */ void *data; /* adress for data */ UINT data_len; /* lenght for this data in bytes */ UINT total_len; /* total lenght for all data in datalist */
} DATALIST; Вернее указателями на списки Цитата(OlegHmt @ May 4 2008, 03:30)  Писать свой менеджер памяти, пока для меня будет сложно, поэтому разбираюсь с lwIP. Правда ещё попробую копировать по 4 байта. А пока такой вопрос - прошивка у меня компилируется в Thumb виде, согласно документации - использование ARM команд повышает быстродействие по сравнению с Thumb режимом. Пока что, при компиляции в ARM у меня система перестаёт работать - запускается нормально, но при попытке объявить (или запустить, ещё не разобрался) какую-либо задачу (использую FreeRTOS) - виснет. Соответсвенно вопрос - стоит ли этим баловаться (в смысле компиляцией в ARM) и в чём может быть проблема, что у меня подвисает система (может я не учёл что-либо общеизвестное)? FreeRTOS не лучшая ОСь для повышения скорости. Если с lwIP более-менее разобрались, почему бы не выбрать что-нить (OS) побыстрее и посеръезнее? Попробуйте перевести проект на ucOS-II. Наверняка получите еще несколько десятков попугаев, а возможно и исчезнут проблемы при компиляции в ARM-режиме, хотя как уже сказали выше, толку не будет из за пакетов-венегретов
Сообщение отредактировал prottoss - May 4 2008, 13:04
--------------------
|
|
|
|
|
May 4 2008, 13:58
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Rst7 @ May 4 2008, 14:06)  Вы уж простите, что лезу, но вменяемые компиляторы делают совсем другой код: Да дело не во вменяемости. Компилил вменяемым RVCT. Единственное забыл включить оптимизацию. Но даже если взять оптимизированный код приведенный вами, то в ARM получается 4 слова в ключевом цикле, в Thumb - 3. Так что все равно надо смотреть, что будет выполняться быстрее на SAM7 при размещении во флеш. Цитата Стандартные библиотеки писаны далеко не дураками, и не на C. Не важно на чем они писаны. Стандартные библиотеки поддерживают стандарт, стандартная memcpy просто обязана делать проверки на выровненность данных и не выходить за границы копируемого участка, чтобы ничего лишнего не перетирать. Надо ли отметить, что опустив эти проверки, получается дополнительный выигрыш в скорости? Цитата Не надо уходить от memcpy! Тут все по-своему правы. Уходить надо, там где важна скорость. В этой ветке скорость важна, значит уходить надо. Если важна универсальность, чистота и прогнозируемость кода, а на скорость можно забить, тогда можно и не уходить. Цитата(prottoss @ May 4 2008, 16:01)  Ну тогда объясните, чем memcpy лучше обмена списками вида Есть места где от копирования не уйти. Если DMA не умеет самостоятельно склеивать списки в пакет, то "memcpy" как минимум понадобится для копирования ethernet/ip header'a в каждый пакет. Цитата(zltigo @ May 4 2008, 13:45)  Для изначально "правильных" программ Thumb у меня ПРОИГРАЛ. По объему кода?
|
|
|
|
|
May 4 2008, 14:55
|

Гуру
     
Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659

|
Цитата(defunct @ May 4 2008, 21:26)  Ну как же нет. Ключевая фраза у Вас - "заполняем каждый блок своим хидером". Как заполняем? быстрее и логичнее всего - скопировать заранее подготовленный шаблон. Стало быть от копирования нельзя уйти. Хедер TCP я заполняю примерно так: Код /* Fill IP header */ hIP.vihl = socket->vihl; hIP.tos = socket->tos; hIP.tlen = SWAP16(sizeof(IP_HEADER) + sizeof(TCP_HEADER) + data_len); hIP.id = IP_GetNextPacketID(); hIP.frags = 0; hIP.ttl = socket->ttl; /* ttl */ hIP.protocol = IP_TCP; /* packet protocol */ hIP.src_ip = HOST_IP(); /* My IP */ hIP.dest_ip = arp_entry->ip; /* Dest IP */ /* Make checksum */ hIP.checksum = 0; hIP.checksum = (UINT16)~IP_MakeChecksum(0, &hIP, sizeof(IP_HEADER)); Код /* Fill TCP header */ hTCP.src_port = socket->src_port; hTCP.dest_port = socket->dest_port; hTCP.seqno = SWAP32(socket->send_unacked); hTCP.ackno = SWAP32(socket->receive_next); hTCP.hlen = (sizeof(TCP_HEADER) << 2) & 0xfc; hTCP.window = SWAP16(socket->window); hTCP.urgent = 0; if(flags) /* If flags are sets, we update them, differently we leave old */ hTCP.flags = flags; hTCP.checksum = TCP_MakeChecksum(&hIP, &hTCP, data); hIP и hTCP - соответсвенно блоки прамяти для IP и TCP заголовка, и мне не понятно, с какого шаблона я их сформирую:-) Ну и, думаю, что БЫСТРЕЕ и логичнее заполнить именно так
--------------------
|
|
|
|
|
May 4 2008, 14:59
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(prottoss @ May 4 2008, 17:55)  hIP и hTCP - соответсвенно блоки прамяти для IP и TCP заголовка, Ок, как часто у вас поменяется Ethernet header и сколько полей IP заголовка в пределах сессии? То, что не меняется - и будет шаблоном. Цитата и мне не понятно, с какого шаблона я их сформирую:-) Ну например в структуре "socket" резервируете 14 + 20 байт +2 байта GAP. единожды заполняете Eth/IP header - это будет шаблон. далее выделяете блок для Eth/IP, копируете туда шаблон спец функцией расчитаной на быстрое копирование выровненных блоков длиной 9 слов. Меняете поле Len и пересчитываете CS. Уже будет гораздо быстрее чем заполнение для каждого пакета.
|
|
|
|
|
May 4 2008, 15:15
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(defunct @ May 4 2008, 17:58)  Надо ли отметить, что опустив эти проверки, получается дополнительный выигрыш в скорости? Оверхед от проверок минимальный, если, конечно, копируются не блоки по 8 байт. Цитата(defunct @ May 4 2008, 17:58)  Тут все по-своему правы. Уходить надо, там где важна скорость. В этой ветке скорость важна, значит уходить надо. Если важна универсальность, чистота и прогнозируемость кода, а на скорость можно забить, тогда можно и не уходить. ИМХО, если уходить значит вообще не копировать, тогда конечно. Но уделать по скорости копирования нормальную memcpy очень трудно.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|