Блог программиста

Archive for the ‘Решения’ Category

Общий код.

Вторник, Август 26th, 2008

Вот и отпустило немного на работе, в свзяи с чем продолжим.

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

Так и в нашем случае, при изменениях в оборудовании куски кода отделяются #define диррективами. Это решение на уровне компиляций позволяет поддержать достаточно большое колличество версий, но заставляет постоянно создавать все большее колличество сборок (билдов), каждая из котороых отвечает за определенную версию железа.
Однако, частенько такое решение не подходит, так как наше оборудование вполне может быть динамически заменяемым, апгрейднутым юзером, что накладывает на наши драйвера и прочий код требования поддержать различные версии в ран-тайме.

Решение тут тоже обычное, при создании драйверов, внешний интерфейс превращается в набор указатей на стандартные функции, заполнение которых зависит от того типа девайса, который найден при старте системы.
Но, что делать если код уже однажды был кем-то (возможно и вами) нписан как раздельный для различных сборок и надо его привести к общему знаменателю? Или если код чужой? Т.е. измненения контекста или наименования функций приводят к многократной переделке и замене всего и вся в самом коде, что уже довольно большой риск?
(more…)

Про память.

Четверг, Июль 3rd, 2008

Помните, я уже писал о работе с памятью. Там было теоретически.
Сейчас столкнулся с инетересным (тоже теоретически. практически - убил бы) строением пулов. Двунаправленые.
Коротко, имеется два варианта.
1. Есть две разные структуры данных одинакового размера (например 32 байта). Выделенная память отдается обоим. При этом первая получает стартовый адрес в пуле и продвигается ++. Вторая получается самый низкий (т.е. адрес = конец пула - размер одного блока) адрес и продвигается –.
2. Во втором варианте структуры жанных разного размера.

Все. Память в пул никогда не возвращается, изначальной стартовой нарезки не происходит. Колличество элементов связано друг с другом логически, и примерно опредеяет работу по заниманию памяти.
При необходимости вернуть память обратно, т.е. объявить ее логически свободной, элементы добавляются в связные списки свободных блоков, которые используются тоже (реюзинг).

Минусы такой системы ясны невооруженным взглядом.
1. При нежесткой лигической связке или наличии разых условий связывания - размеры (точнее колличество элементов каждого типа данных) непредсказуемы.
2. Однажды полученый максимум одного из пары никогда не отдаст своих (даже равных по размеру) блоков своему партнеру по паре. Т.е. имея свободную память мы не сможем ее аллоцировать, и при этом (см. п.1) размеры непредсказуемы и зависят от внешних условий.
3. При разных размеров блоков (если они еще и не кратны) возможны небольшие потери свободного места памяти посредине при максимальной выборке.
4. В коде постоянные проверки на наличие вободных модулей в списках для реюзинга.

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

Организация сбора данных и экономия памяти.

Воскресенье, Июнь 1st, 2008

Как известно многим практикам, есть страшная и неотвратимая зависимость скорости работы и размера используемой памяти. Часто - это проблема нетривиальна, и однозначного рещения не имеет. Коротенький пример для тех, кто с проблемой не знаком, или знаком плохо.

Пример

Задача - принять данные с 256 портов и передать их соответственно в другие 256 портов.

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

(more…)