Перейти к содержанию
    

Как повысить скорость работы по сети AT91SAM7X256

Некоторое время назад я разрабатывал прошивку для этого процесора в которой была добавлена функциональность обмена данными по сети и USB. В качестве операционной системы использована FreeRTOS, TCP/IP стека - uIP. Сейчас потребовалось повысить скорость обработки событий устройством и оказалось, что по сети обмен данными идёт медленно. Так как я разбирался со всеми нюансами процесора и внутренностями сети самостоятельно, то мог пропустить какие-то важные моменты. Может кто увидит какие-то недостатки приведённого ниже алгоритма и подскажет как увеличить скорость работы устройства.

Итак, в устройство периодически по сети передаются данные блоком размером приблизительно в 16-20 кБ. Эти данные записываются в масив памяти и по таймеру процесора обрабатываются.

Когда я програмировал работу с сетью то передачу данных сделал блоками по 1500 Байт. Может я что-то не учёл, а может тогда вылезали другие ошибки, но при передачи блока большей длины у меня подвисал процесор (приёмный буфер установлен приблизительно на 2кБ), поэтому сделать так как в USB, когда я пишу в порт всё одним куском, на уровне USB контролера масив разбивается на части, а в процесоре я уже собираю данные (гарантированно доставленные), мне не удалось. Сейчас я смотрю, что похоже можно в обработчике прерываний от сети поставить свой код анализа входных данных и сделать похоже как в USB но не уверен - не буду ли я получать повреждённые даные, которые долго нужно будет перефрагментировать, проверять и т.д.

Сейчас у меня скорость передачи данных приблизительно 2Мб/с по 100 мегабитной сети. Выглядит это приблизительно так (результат теперешнего анализа с помощью Ethereal): отсылается пакет размером в 1500 байт в процесор, приблизительно через 4мс (время работы стека и моего копирования порции в нужное место памяти) процесор отсылает однобайтный пакет-подтверждения о приёме данных (чтобы внешняя программа не начала посылать данные пока я не обработал предыдущую порцию - правильно ли?), приблизительно через (дома забыл логи поэтому тут цыфру не помню) толи 1, толи 0,1мс, отсылается следующая порция данных. В результате 16кБ передаётся приблизительно за 50мс. Долго, было бы хорошо хотя бы в 2 раза быстрее. Можно ли улучшить скорость? Я смотрел, что при обработке стек копирует данные из буфера контролера в память, а потом я копирую куда мне нужно. Может можно сразу копировать без промежуточного буфера, но не будут ли проблемы с проверкой ошибок при передаче?

Может в стеке uIP можно убрать лишнюю обработку?

 

Пока что я в процессе анализа кода, может ещё что и сам найду, но за любые советы и предложения буду искренне благодарен.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...

Сейчас у меня скорость передачи данных приблизительно 2Мб/с по 100 мегабитной сети.

...

 

Сначала тоже юзали uIP, скорость была еще ниже. Потом подняли lwIP стек, включили все кеши (АТ91RM9200) - сейчас 64 Мбита, больше - никак :(

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 мегабита или 2 мегабайта ?

Мегабита, ну может там до 3 Мегабит

 

В принципе вопрос этого плана я когда-то поднимал и был ряд ответов, но тогда эта часть проекта была отложена, поэтому ничего не исправлялось. К этому моменту я уже немного позабыл, что там делалось и теперь со свежым взглядом на сетевую часть проекта пробую её улучшить :)

 

Сначала тоже юзали uIP, скорость была еще ниже. Потом подняли lwIP стек, включили все кеши (АТ91RM9200) - сейчас 64 Мбита, больше - никак :(

 

Процесор немного не тот, но, в принципе, в этом что-то есть. Все таки uIP это восьмибитный, а lwIP - 32-х битный, но пока что стек менять очень не хочется, да ест lwIP больше памяти, а с этим напряжёнка

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А транспорт UDP или TCP?

Мы юзали uCOS у нас с подправленным стеком на UDP получалась скорость порядка 900кбайт/с проц на 48 МГц, делал похожую задачу, собирал данные с АЦП и пихал их в сетку. За счет TCP стека скорость падала вдвое. Все работало через PDU. С АЦП данные пихались прямо в выходной буфер, а оттуда при наборе нужного количества выпихивались в Ethernet. Мониторинг загрузки проца показывал 98%, из чего можно сделать заключение, что добавление хоть каких то дополнительных задач привело бы к уменьшению пропускной способности. Причем зависимость экспоненциальная, добавили моргание светодиодом и обработчик внешнего прерывания и скорость упала до 400килобайт/с.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А транспорт UDP или TCP?

Мы юзали uCOS у нас с подправленным стеком на UDP получалась скорость порядка 900кбайт/с проц на 48 МГц, делал похожую задачу, собирал данные с АЦП и пихал их в сетку. За счет TCP стека скорость падала вдвое. Все работало через PDU. С АЦП данные пихались прямо в выходной буфер, а оттуда при наборе нужного количества выпихивались в Ethernet. Мониторинг загрузки проца показывал 98%, из чего можно сделать заключение, что добавление хоть каких то дополнительных задач привело бы к уменьшению пропускной способности. Причем зависимость экспоненциальная, добавили моргание светодиодом и обработчик внешнего прерывания и скорость упала до 400килобайт/с.

 

TCP

У меня там и моргание, и паралельные задачи, и обработка прерываний по таймеру где-то под 30кГц

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Закончил примерно с месяц встраиваемый HTTP-сервер + SNMP-агент на AT91SAM7X256. Стек использовал свой - давным-давно переделанный OpenTCP. Самый большой тормоз, ИМХО, в таких приложениях - использование memcpy, хотя МК распологает возможностью не использовать данную функцию. Код выложить не могу, ибо мое. Идея относительно простая:

 

1. Пишем свой менеджер памяти для работы с блоками по 128 байт (размерность для принимаемых блоков EMAC SAM7X).

 

2. Выделяем драйверу EMAC и стеку TCP/IP блоки.

 

3. При получении данных драйвер отдает список блоков с принятым пакетом а стек драйверу взамен свободные блоки. При этом производится обмен только указателями. Как такового не нужного копирования данных не происходит.

 

4. Соответсвенно, приложения(задачи) верхнего уровня (могут быть) тоже заточены под такой обмен данными.

 

При отправке данных опять происходит обмен указателями между драйвером и стеком(приложением).

 

 

 

Скорость, честно говоря не мерял, но работает довольно шустро - сетка прнимерно из 3-х сотен машин

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Закончил примерно с месяц встраиваемый HTTP-сервер + SNMP-агент на AT91SAM7X256. Стек использовал свой - давным-давно переделанный OpenTCP. Самый большой тормоз, ИМХО, в таких приложениях - использование memcpy, хотя МК распологает возможностью не использовать данную функцию. Код выложить не могу, ибо мое. Идея относительно простая:

 

1. Пишем свой менеджер памяти для работы с блоками по 128 байт (размерность для принимаемых блоков EMAC SAM7X).

 

2. Выделяем драйверу EMAC и стеку TCP/IP блоки.

 

3. При получении данных драйвер отдает список блоков с принятым пакетом а стек драйверу взамен свободные блоки. При этом производится обмен только указателями. Как такового не нужного копирования данных не происходит.

 

4. Соответсвенно, приложения(задачи) верхнего уровня (могут быть) тоже заточены под такой обмен данными.

 

При отправке данных опять происходит обмен указателями между драйвером и стеком(приложением).

 

 

 

Скорость, честно говоря не мерял, но работает довольно шустро - сетка прнимерно из 3-х сотен машин

 

Очень хороша идея.

Главное, что сам похожие вещи делал, при чём в этом же проекте, а тут использовать не догадался. Попробую :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

в таких приложениях - использование memcpy

Да от memcpy надо уходить.

Но даже с копированием, если сразу по 4 байта, и без проверок на выход за границы скорость значительно возрастет.

 

e.g.:

 

static void memcpy_burst( U32 *pDest, U32 *pSrc, U32 size )
{
    U32  wCnt = (size + 3) >> 2; // words count to be copied
    do
    {
        *pDest++ = *pSrc++;
    } while (--wCnt);
}

 

данные обязательно должны быть 32bit-aligned.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Какую скорость требуется получить ?

 

на uip много не вытяните, нужно lwIp или ucTCP/IP.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Какую скорость требуется получить ?

 

на uip много не вытяните, нужно lwIp или ucTCP/IP.

 

 

думаю раза в 2 подойдёт (5-6Мбит/с). Попробую выбросить копирование данных и сделаю всё на обмене указателями, а дальше будет видно по результатам

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Писать свой менеджер памяти, пока для меня будет сложно, поэтому разбираюсь с lwIP. Правда ещё попробую копировать по 4 байта. А пока такой вопрос - прошивка у меня компилируется в Thumb виде, согласно документации - использование ARM команд повышает быстродействие по сравнению с Thumb режимом. Пока что, при компиляции в ARM у меня система перестаёт работать - запускается нормально, но при попытке объявить (или запустить, ещё не разобрался) какую-либо задачу (использую FreeRTOS) - виснет. Соответсвенно вопрос - стоит ли этим баловаться (в смысле компиляцией в ARM) и в чём может быть проблема, что у меня подвисает система (может я не учёл что-либо общеизвестное)?

Изменено пользователем OlegHmt

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А пока такой вопрос - прошивка у меня компилируется в Thumb виде, согласно документации - использование ARM команд повышает быстродействие по сравнению с Thumb режимом.

Справедливо для быстрой памяти, т.е. если ARM и Thumb инструкции будут выбираться одинаково быстро. Но для SAM7 это не так. Надо помнить что слабое место у SAM7 это флеш. В ARM режиме размер кода увеличивается в среднем на 30%, а это значит как минимум на 30% больше обращений к медленной флеш. Плюс в SAM7 применена технология ускоренной выборки для Thumb режима (по две Thumb инструкции за одно обращение к флеш). Т.е. при размещении программы во флеш Thumb режим заведомо в выигрыше.

 

Кроме того при прочих равных (если код выполнять из RAM), для IP стека ARM режим практически не даст никакого выигрыша, возможно даже проиграет, из-за big-endian формата данных в пакетах и множества byte и half-word полей.

 

В ARM режиме резонно написать некоторые особо критические функции, и разместить их в RAM. Но надо смотреть каждую конкретную функцию, т.к. например функция копирования приведенная выше и в ARM режиме и в Thumb компилуруется в 16 инструкций. Листинги привожу ниже:

 

ARM mode

   258: static void memcpy_burst( U32 *pDest, U32 *pSrc, U32 size ) 
   259: { 
0x00001EC0  E92D0010  STMDB     R13!,{R4}
0x00001EC4  E1A03002  MOV       R3,R2
   260:     U32  wCnt = (size + 3) >> 2; // words count to be copied 
0x00001EC8  E1A02003  MOV       R2,R3
0x00001ECC  E2822003  ADD       R2,R2,#0x00000003
0x00001ED0  E1A02122  MOV       R2,R2,LSR #2
   261:     do 
   262:     { 
   263:         *pDest++ = *pSrc++; 
0x00001ED4  E1A03001  MOV       R3,R1
0x00001ED8  E2831004  ADD       R1,R3,#0x00000004
0x00001EDC  E5934000  LDR       R4,[R3]
0x00001EE0  E1A03000  MOV       R3,R0
0x00001EE4  E2830004  ADD       R0,R3,#0x00000004
0x00001EE8  E5834000  STR       R4,[R3]
   264:     } while (--wCnt); 
0x00001EEC  E1A03002  MOV       R3,R2
0x00001EF0  E2433001  SUB       R3,R3,#0x00000001
0x00001EF4  E1A02003  MOV       R2,R3
0x00001EF8  E3530000  CMP       R3,#0x00000000
0x00001EFC  1AFFFFF4  BNE       0x00001ED4
   265: }

 

Thumb mode

   258: static void memcpy_burst( U32 *pDest, U32 *pSrc, U32 size ) 
   259: { 
0x00001EC0  B410      PUSH      {R4}
0x00001EC2  1C13      ADD       R3,R2,#0
   260:     U32  wCnt = (size + 3) >> 2; // words count to be copied 
0x00001EC4  1C1A      ADD       R2,R3,#0
0x00001EC6  3203      ADD       R2,#0x03
0x00001EC8  0892      LSR       R2,R2,#2
   261:     do 
   262:     { 
   263:         *pDest++ = *pSrc++; 
0x00001ECA  1C0B      ADD       R3,R1,#0
0x00001ECC  3104      ADD       R1,#0x04
0x00001ECE  681C      LDR       R4,[R3,#0x00]
0x00001ED0  1C03      ADD       R3,R0,#0
0x00001ED2  3004      ADD       R0,#0x04
0x00001ED4  601C      STR       R4,[R3,#0x00]
   264:     } while (--wCnt); 
0x00001ED6  1C13      ADD       R3,R2,#0
0x00001ED8  3B01      SUB       R3,#0x01
0x00001EDA  1C1A      ADD       R2,R3,#0
0x00001EDC  2B00      CMP       R3,#0x00
0x00001EDE  D1F4      BNE       0x00001ECA
   265: }

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Понятно, тогда вопрос со стороны lwIP - можно ли использовать для моей задачи пример использования стека из FreeRTOS? Первоначальное знакомство с примером показало, что в нём используются очереди, а в предыдущей версии ОС, когда я разбирался с обменом данными по USB, используя пример из FreeRTOS, то как раз очереди очень сильно тормозили обмен. И только когда я заменил их на обычные масивы - тогда смог выйти на расчётные скорости обмена данными.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Первоначальное знакомство с примером показало, что в нём используются очереди,

А чем очередь принципиально отличается от массива?

Это же массив указателей и пара индексов put/get.

В любом случае Вы вольны написать свои обёртки очередей.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...