Записи с темой: программирование (список заголовков)
23:59 

Больше студий для бога студий

Отлаживать одновременно две программы -- норма. Допустим, клиент и сервер.

Сегодня я дошёл до отладки сразу четырёх программ:
1. Прошивка микроконтроллера;
2. Основная программа на компьютере;
3. DLL-ка, через которую основная программа связывается с устройством на микроконтроллере;
4. Программа по обработке полученных результатов (ей занимаюсь не я, но мне надо было посмотреть, что в ней происходит, т.к. она не работала).

Ну и ещё пятый проект был открыт для справки.

@темы: Программирование

23:59 

Как говорить с младенцем о семиотике

Долго ли, коротко ли, решили мы отдать исходный текст одного проекта другой организации.

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

Я думаю -- ого. Сейчас нам пришлют гуру. Мы с ним потолкуем, я чему-нибудь новому научусь.

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

А он отвечает -- да я вообще не программист. И программирования совсем не знаю.

Да вы шутите! Как же я ему рассказывать буду?

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

Об'яснил три раза. Задаю контрольный вопрос. И всё. Оказывается, что он даже на этом уровне ничего не понял.

У меня появилось острое ощущение, что я принимаю экзамен у студента, который не готовился. А мужик за сорок.

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

Я сказал, что конечно, но мне всё равно было смешно.

@темы: Программирование, Случай из жизни

23:59 

Альтернативный метод работы с таймерами в микроконтроллерах

Я хочу рассказать историю одного говнокода, но сначала вводная.

(кручу-верчу, обмануть хочу)

@темы: Борьба с техникой, Говнокод, Программирование

23:59 

Скучное расследование тормозов при передаче данных

23:59 

Между первой и второй перерывчик небольшой

В WinAPI есть функция SetTimer. Она получает в качестве аргумента время в миллисекундах. И через заданное число миллисекунд генерирует событие, которое можно обработать.

Уменьшаю время. Уменьшаю. А эффекта нет. Полез в документацию.

ОКАЗЫВАЕТСЯ, что у SetTimer есть минимальное время, равное 10 мс. Если задавать меньше, всё равно событие будет генерироваться каждые 10 мс. Да, в документации про это написано. Однако поведение очень неожиданное. Например у функции Sleep, которая просто приостанавливает выполнение программы на заданное время, таких ограничений нет.

Интересно, это ограничение родилось из времён, когда компьютеры были большими? Современные компьютеры и таймер в 1 мс спокойно обработают.

@темы: Говнокод, Программирование

23:59 

2-НДФЛ

Отлаживаю передачу данных из микроконтроллера в компьютер.

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

Я (сам себе): Так, я должен получать 600 кусков. А получаю только 500 кусков. И не понимаю, почему.
Коллега: Ты свою зарплату считаешь?

@темы: Случай из жизни, Программирование, Викторика

23:59 

Метод вытянешь -- именованная труба увязнет

Я: Делал то-то и то-то. Сделал. Работает. Но вот другая штука сломалась. Сейчас разбираюсь, почему.
Начальник: Как это так получилось, что делал одно, а сломалось другое?
Я: А у вас что, так не бывает?
Начальник: Нет, не бывает.
Я: Может быть, вы мало программированием занимаетесь?
Начальник: Двадцать лет... да, наверное, мало.

Тут подключается наш третий программист:
Третий: У меня тоже такого не бывает.
Я: Что, совсем?
Третий: Ну... бывает всё-таки. Редко.

***

Вопрос, в первую очередь, к программистам -- часто ли у вас выходит из строя одна часть программы, когда вы делаете другую? Как с этим бороться?

У меня такое случается часто, особенно при работе с проектами, у которых, так скажем, богатая история.

@темы: Программирование

23:59 

Об ошибках в POS-терминалах

POS-терминалы, банкоматы и другие подобные устройства стали весьма распространены. Очевидно, информация, которая на них показывается, чем-то генерируется. Как правило, в основе подобных устройств лежит обычный ПК, хотя иногда встречаются исключения.

Поскольку в основе -- ПК, а на нём крутится винда и какое-то ПО, всё это изредка падает. Выводятся соответствующие сообщения об ошибках, синие экраны и прочее.

И вот этот момент, когда служебный экран вместо основной функции начинает показывать винду или какое-нибудь неположенное окошко, вызывает ощущение разрыва реальности. Этого тут быть не должно! Как будто декорация отвалилась. И это и есть отвалившаяся декорация -- в прямом смысле!

Не я один дивлюсь на такое, в интернете куча фоток. Я сам тоже снимаю, если вижу. Вот, например, захожу я как-то раз в одно заведение и вижу там на кассе вот что:



Хотя испытать удивление в связи с нетипичной ситуацией -- это очень хорошо, но для владельцев экранов отвалившаяся декорация -- это плохо. Хотя все всё понимают, но демонстрация внутренностей всё равно снижает доверие к владельцам. Непорядок у них!

Как же должно вести себя ПО на компьютерах, экраны которых постоянно видят клиенты?

1. Желательно, чтобы сообщения об ошибках на такие экраны не выводились вообще. Должно быть просто написано "устройство временно не работает". Примерно так пишут банкоматы Сбербанка. Клиентам незачем знать, что там сломалось. Если устройство предназначено для пассивного вывода информации, либо работают с ним только сотрудники, но не клиенты, то можно вообще выводить логотип организации без пояснений. Тогда клиенты даже не догадаются, что что-то сломано. Очевидно, сотрудники должны уметь отличать логотип, сигнализирующий об ошибке, от обычного.
2. Естественно, работники, либо сервисный инженер, должны иметь возможность узнать, что произошло на самом деле. Это можно реализовать различными способами, к примеру, если нажать определённую комбинацию клавиш на терминале, откроется окно с текстом ошибки. Также возможна запись в лог-файл и последующий дистанционный его просмотр.
3. Тексты сообщений об ошибках должны быть на простом русском языке. Если работник в состоянии сам исправить ошибку, по тексту должно быть понятно, что делать ("отсутствует питание: вставьте вилку в розетку"). Если сообщение предназначено для сервисного инженера, оно должно легко читаться вслух, чтобы инженер мог оказать помощь по телефону. Проще всего выводить код ошибки.

Философский вопрос, следует ли перезапускать программу, если возникла ошибка? Или надо держать её в состоянии ошибки до прибытия помощи? Я считаю так: если ошибка предусмотрена разработчиками и корректно обработана И нет серьёзной необходимости, чтобы ПО постоянно работало, то пусть себе висит в состоянии ошибки. Если же ошибка не предусмотрена (типа access violation) либо если ПО должно работать 24/7, то лучше программу перезапустить. То есть:

4. Должна быть специальная сторожевая программа, которая проверяет, жива ли основная. Сторожевая программа должна уметь определять нестандартные ситуации (и особенно access violation) и при малейшем подозрении перезапускать основную программу. Основная программа, в свою очередь, должна проверять, жива ли сторожевая, и запускать её, если упала уже она.
5. Продолжение 4. Предусмотренные сообщения об ошибках должны выводиться в стиле основного интерфейса. Любые стандартные окошки винды, панель задач и т.п. -- недопустимы. Если основная программа падает всё время, сторожевая должна запускать альтернативную программу, которая будет уметь показывать на экране только полноэкранный логотип на переднем плане. Шанс, что такая программа сфейлится в работе, гораздо ниже, чем шанс фейла основной.

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

@темы: Программирование, Мысли, Восприятие

23:59 

Как работает стек в PIC32 (MIPS)?

По-видимому кроме STM32 (ARM) теперь я буду работать ещё и с PIC32 (MIPS). Стал разбираться, что к чему. И что-то оказалось, что в MIPS всё не как у людей. Про статусное слово процессора ничего не видно. Про стек ничего не видно. Что происходит вообще?

Со статусным словом я потом разобрался -- оно хранится в сопроцессоре! И в нём нету флагов результатов арифметической операции. Но ладно, команды ветвления и без этого работают, проверяя результат напрямую.

А вот со стеком... в общем, стека в MIPS действительно нет. Программист, если хочет, может реализовать его в виде программной эмуляции. Ясное дело, что стек всем нужен, поэтому компиляторы Си автоматически делают эту эмуляцию. Выглядит это примерно так:

Каждый раз, когда нужен push:

1. Вычесть из регистра, который назначен регистром стека, четыре.
2. Положить по адресу в этом регистре нужное число.

Каждый раз, когда нужен pop:

1. Забрать по адресу из регистра число.
2. Прибавить к регистру четыре.

ААА!

Из-за этого оверхеда один товарищ, который давно с pic32 работает, старается локальные переменные вынести по максимому в глобальные. Типа для скорости. Потому что локальные переменные хранятся на стеке. Хотя так ли велика потеря?

UPD. Потеря такова.

1. Согласно документации, пуш и поп в STM32 (ARM) занимают 1+N тактов, где N -- число сохраняемых регистров (они задаются списком). См, например, Cortex M-4 r0p0 Technical Reference Manual, Issue B, p. 3-6, Table 3-1.
2. Судя по описанию работы конвейера в PIC32, все команды кроме команд умножения, деления и FPU выполняются за один такт. Хотя мне не удалось найти, где про это написано явно.

Таким образом, оверхед при сохранении/загрузке одного регистра одинаковый (без учёта конвейерной оптимизации). Если регистров несколько, то у STM32 (ARM) небольшое преимущество, однако:
1. В зависимости от реализации конвейера в конкретном PIC32 а также получившегося кода общее время выполнения при той же частоте у PIC32 может оказаться даже меньше.
2. Далеко не вся работа со стеком заключается в использовании пуш и поп. Если локальная переменная хранится в стеке, то обращение к ней будет просто load/store командой, которая занимает 2 такта в STM32 (ARM) и, по-видимому, 1 такт в PIC32. Кроме того, работа с переменными в стеке в таком случае не будет отличаться от работы с глобальными переменными (по времени).
3. При таком большом количестве регистров общего назначения появляется возможность размещать часть локальных переменных в регистрах процессора, а не в стеке. И у PIC32 возможности тут шире, т.к. регистров больше.

Таким образом, отказ от локальных переменных в пользу глобальных для ускорения работы программы необоснован.


Вот я не знаю, может быть, я что-то не понимаю в архитектурах, но почему нельзя было сделать встроенные пуш и поп? Что-что, говорите? Потому что это RISC? Так ARM тоже RISC. И там есть отличные пуш и поп.

@темы: Программирование, Крик души, Говнокод, Борьба с техникой

23:59 

О цветах в программах

Лучше всего, конечно, полагаться на системные настройки. Но иногда хочется добавить каких-нибудь красивостей в оформлении. Чтобы всё было жёлтым написано по красному, к примеру.

И вот какую ошибку я хочу предупредить. Берясь за цветовое оформление, следует забирать под своё крыло его целиком. В базовом случае -- и цвет шрифта, и цвет фона. Потому что системный цвет фона на вашем компьютере может отличаться от цвета фона у пользователей. И тогда может получиться голубой текст на сером фоне. Это еле читаемо. А у вас всё было хорошо, потому что стояла цветовая схема с чёрным фоновым цветом.

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

А всё это я встретил в программе ASUS AiSuite 3. Ей большой привет. При этом у меня стояла стандартная схема оформления. Читаемый вариант я получил только переключившись на высококонтрастную.

@темы: Программирование, Говнокод

23:52 

Без наркоза

Медианная фильтрация -- отличная штука. Спасает от случайных выбросов в данных.

В Матбале, естественно, есть готовая функция. А в Си -- нету. Мне сказали -- а возьми код из нашего соседнего проекта. Там большая, проверенная математическая библиотека есть. Мы её сами писали в своё время.

(что было дальше)

@темы: Борьба с техникой, Говнокод, Программирование

23:59 

О версионности

Приносят прибор на основе микроконтроллера. Неправильно работает.

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

Потом я догадался -- в прибор была прошита старая прошивка. Это был единственный прибор, который я ещё не прошил исправленной версией.

Так я познал очевидные истины:
1. Версии прошивки должны нумероваться (и быть приуроченными к коммитам!).
2. Устройство должно уметь сообщать о своей версии.
3. Изготовителю надо вести учёт, в каком из приборов какая прошивка стоит (если, конечно, пользователи не могут прошивать прибор самостоятельно).

Версионность для программ под обычный компьютер тоже актуальна, но не так, как под контроллеры: когда есть ОС и файловая система, версию можно опознать по косвенным признакам -- размеру и дате изменения. В контроллере же нет ни того, ни другого, ни третьего, ни четвёртого.

@темы: Борьба с техникой, Лайфхак, Фейлы, Программирование, Очевидное-невероятное

23:59 

Пьер Менар

При переписывании кода с MATLAB на Си, надо помнить самое главное правило:

В Си индексация массивов всегда начинается с нуля. А в MATLAB всегда начинается с единицы!

После исправления всех ошибок индексации сходство результатов достигает 5 и более значащих цифр.

@темы: Борьба с техникой, Программирование

23:59 

Аппарат абонента не существует

Приделал к проекту очередную фишку. Было свободное время, т.к. коллега свою часть ещё не доделал. Дай, думаю, посмотрю, какие предупреждения выдаёт компилятор. Я человек простой -- предупреждения компилятора, как правило, игнорирую. Но знаю одного человека, который не начинает отладку, пока не избавится от всех предупреждений.

Так вот, он прав.

Короче говоря среди кучи declared but never used и incompatible pointer type (я слышу возглас из зала: "тебе пора переходить на Rust!") я обнаружил действительно серьёзную вещь. "returning pointer to local variable".

Эта штука была в участке кода, который я с удовольствием скопировал из интернета. Кто же тут лоханулся, я или автор кода? Конечно же, я.

Автор кода выделял динамический массив:

int a=new int[n];

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

int a[3];

И удалил строку с освобождением памяти. А что происходит с этим массивом -- не посмотрел. В общем, он возвращался из функции:

return a;

И если при динамическом выделении памяти всё было бы хорошо, то при локальном (автоматическом) выделении памяти всё становится плохо. Функция завершается, освобождает память на стеке, а вызывающая функция начинает обращаться к ничьей области памяти.

* * *

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

@темы: Говнокод, Программирование

23:59 

Программа, электроника и механика: надёжность

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

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

С отладкой механических устройств я по работе почти не сталкивался. Но в те пару раз, когда это всё-таки происходило, я обнаружил ещё более интересную вещь: неполадки в механической части ещё сложнее диагностировать и устранить, чем в электронной. Штанги не доходят туда, куда нужно (а если доходят, то не фиксируются в нужном положении). Колёсики заедают. А "посмотреть", что происходит на самом деле -- возможности практически нет.

Однако, существует широко распространённое мнение, что если устройство функционирует чисто механически -- то это "на века". Электронные приборы гораздо нежнее и легко выходят из строя. А уж программа -- это вообще что-то эфемерное. Каждый первый разработчик ПО в лицензионном соглашении пишет, что отказывается от ответственности за то, что сотворила его программа (и за то, что не сотворила, тоже). А программы, за которые разработчик отвечает своей шкурой, стоят совсем других денег.

Откуда же берётся, что механическое устройство такое надёжное? Дело в сложности. Те механические устройства, которые надёжные, содержат в себе, к примеру, сотни элементов. Они перемещаются, к примеру, 10 раз в секунду, не больше. Или просто вертятся без остановки. А процессор? Сколько в нём транзисторов? Зависит от процессора, но, к примеру, 731 млн (2008 год, Core i7 "Bloomfield"). А частота -- 2 ГГц (он же). Это частота, с которой могут перекидываться транзисторы внутри. Если бы механическое устройство состояло из такого же количества частей, оно бы проработало весьма недолго. Ибо поломка любой детали -- кирдык.

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

С программами дела обстоят несколько иначе. Если программа не зависит от внешних факторов, кроме предусмотренных "входных данных", её надёжность будет примерно такая же, как у электроники, на которой она работает. То есть -- прошивка микроконтроллера даст сбой либо из-за ошибок в самой прошивке (которые относительно просто обнаружить), либо из-за сбоя электроники. Но на практике прошивки редко воспринимаются пользователями как "программа". Когда говорят о надёжности программы, имеют ввиду чаще всего прикладное ПО, работающее под какой-либо ОС. И на этой ОС кроме самой программы тусуется ещё десяток посторонних программ. И всё это взаимодействует друг с другом и с ОС непредсказуемым образом. То есть, выход программы из строя может быть вызван неизвестным количеством внешних факторов (помимо сбоев электроники и ошибок самой программы). Кто же согласится нести ответственность за такое?

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

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

@темы: Программирование, Мысли, Борьба с техникой, Наблюдения, Электроника

22:28 

Скушай Твикс

У программы на микроконтроллере была очень низкая скорость обмена данными с компьютером по USB. Конкретно -- на отправку в компьютер. Отправка производилось чушками по 256 байт. Скорость итоговая составляла около 2.5 KiB/s.

Долго грешил на тактовую частоту USB-модуля, но любые игрища с ней приводили либо к УМЕНЬШЕНИЮ скорости, либо микроконтроллер превращался в тыкву.

Тогда я стал проводить что-то вроде профилирования, чтобы определить, где же теряется время. Обнаружилось, что первый пакет из контроллера приходит в разное время после первого запроса. А все остальные приходят почти точно через 110 мс. Очевидно, проблема в самом контроллере. Такая скорость слишком низка даже для самого младшего стандарта USB.

Я стал думать, нет ли у меня где-нибудь паузы в главном бесконечном цикле прошивки? (на самом деле думал я об этом и раньше, но ничего не нашёл) Прошерстил всё, нашёл в итоге паузу. Но на 10 мс только. После этого пакеты стали приходить ровно через 100 мс.

Явно остальные 100 мс делаю тоже я. Число круглое. Но где? Почему я этого не вижу?

ОКАЗАЛОСЬ, что дело было в команде Pause(delay). А delay была переменной и имела по-умолчанию значение как раз 100. Бинго!

А я-то искал Pause(100) или хотя бы Pause(ЧИСЛО). И глаз за Pause(delay) не зацеплялся -- даже в окне поиска.

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

Что же делала задержка в главном цикле? Она выполняла роль таймера. Задержки МЕЖДУ измерениями, которые должен был производить контроллер. Но потом я перешёл на вызов измерения по прерыванию и прямая задержка оказалась не нужна. Но убрана не была. Измерениям она не мешала, т.к. они проводились в прерывании. А вот на обмен данными влияла.

@темы: Фейлы, Программирование

21:45 

Разгрузочный день

Рекламирую то, что сегодня в гугло-дудлах:

g.co/doodle/jr54y7?ds=cl

Challenge: по крайней мере на 4 и 6 уровнях можно сделать на шаг короче чем то, что заявлено минимальным.

@темы: Программирование, Игры

23:25 

Указатели и адресное пространство for dummies

Понимание сути указателя не может быть "поверхностным" или "глубоким". Либо оно есть, либо его нет.

(читать дальше)

@темы: Программирование, Статьи, Студенты

23:59 

Близнецы

Однажды я ехал в метро на лекцию (читать). Лекцию по Си. Тогда я ещё недостаточно хорошо ориентировался в материале, поэтому в метро я его повторял. Часть материала данной лекции я собирался излагать по книге авторов Фомин и Подбельский (очень её я люблю). В узких кругах книга известная. Она у меня была с собой. Бумажная.

Сижу, читаю. Рядом сидит паренёк и постоянно ко мне в книгу заглядывает. Что ему, блин, нужно?

Я продолжаю изучать книгу.

Доехал до целевой станции, встаю у дверей, а сам на паренька решил посмотреть.

А у него в руках пакет полиэтиленовый. А сквозь него такая же книга просвечивает.

@темы: Программирование, Книги

23:44 

Строить не ломать

Итак, у меня было две версии функции. Одна была отлично откомментирована и хорошо разбита на блоки. Но она не работала. Это было следствие рефакторинга.

Другая работала. Изначальная. Но выглядела мягко говоря... не очень.

Передо мной стояла задача: надо было заставить первую работать.

Было две стратегии:
1. Долгим вдумчивым взглядом пытаться понять, чем же эти функции различаются, и где я облажался;
2. По частям вводить отрефакторенные блоки в старую функцию и следить, когда она перестанет работать.

Второе оказалось эффективнее.

@темы: Программирование, Очевидное-невероятное, Говнокод

Untitled

главная