Перфолента: мемуары
Здесь есть: много букв о разных интересных вещах.
программы;   о проекте;   ссылки  


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

Название стебное, если кто еще сомневается :-)


Псевдографика в HTML

Растровая графика — хорошая штука, но у нее есть один большой недостаток, который добавляет немало забот веб дизайнерам. Она отключается. Практически любой сайт можно сделать малопривлекательным, просто отключив картинки. И, кроме того, она грузится после кода, не масштабируется при парсинге и не всегда выглядит как надо на разных браузерах.

Когда я работал с СММ, мне часто приходилось сталкиваться с подобным. Тогда же я нашел один интересный способ решения большинства проблем с картинками, но какого либо распространения он не получил. А жаль.

Суть способа в том, что графика кодируется таблицами. В самом простом варианте каждому пикселу картинки соответствует ячейка таблицы размером 1×1 с цветом фона, совпадающим с цветом пиксела. Да, это занимает немало места, но есть немало случаев, когда такой перерасход уместен. Например, рамочка вокруг списка программ здесь на главной странице, или заглавие в «Керогазе». Конечно, нет смысла использовать пять мегабайт таблиц вместо ста килобайт картинок, но тридцать килобайт неотключаемых таблиц вместо двух килобайт «расползающихся» гифов — очень даже.

У меня есть две программы для автоматизации кодирования. Они писаны левой ногой на Delphi, и у меня попросту нет желания ими заниматься, но они есть. Вот они: bmp2tab и frame. Первая делает таблицы из .bmp, вторая — рамки. Описания у них нет, но там все очень просто.


Сетевая мат. лексика

Помнится, у W3C был какой-то стандарт на математическую лексику в сети. Но как он называется, где используется и как работает, не помнится совершенно. Наверное, потому что он не используется нигде. Практически все формулы, матрицы, векторы и тому подобные штуки в интернете передаются с помощью все той же растровой графики. В результате, статья представляет собой один .html и десятка два разноразмерных .gif. Неудобно. Тут еще острее проявляется проблема масштабирования. Например, если я подслеповатый математик и размер шрифта у меня в браузере выкручен на максимум, то для того, чтобы разобраться в сложной формуле, записанной миниатюрной картинкой, мне придется искать очки.

Кодирование таблицами и тут помогает немало. Никакого секрета тут нет, просто немного автоматизации — и вот:

x3
i
+
4x2
i
=
N
y
i
Σ
0

Такой пример занимает меньше килобайта кода, виден на любом браузере, масштабируется вместе с текстом. Как по мне, лучше, чем гиф.

Да, программа опять написана за пол часа и на Delphi, поэтому не стоит ждать от нее многого. Простые формулы, матрицы, вектора — и все. Вот она: formula. Описания, конечно же, нет. Если кто-то всерьез намерен ее использовать, лучше написать мне электропочтой и проконсультироваться.


Любимый алгоритм сортировки

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

for(int i=1;i<N;i++)if(a[i-1]>a[i]){swap(&a[i-1],&a[i]);i-=2+(i==0);}

Просто и эффективно.


Забытое решение СЛАУ

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

Когда-то один программист почтенного возраста упоминал о рекурсивном способе решения СЛАУ. Тот способ легко и безаварийно определял факт решаемости системы, достаточно быстро решал ее. Увы, ни в одной книге я больше не находил упоминания об этом способе. Пришлось придумывать самому. У меня получилось вот так:

float a(int i,int j,int n){
  if(n==N)return A[i][j]; else
  return a(i,j,n+1)*a(n,n,n+1)-a(i,n,n+1)*a(n,j,n+1);
}

float b(int i,int n){
  if(n==N)return B[i]; else
  return a(n,n,n+1)*b(i,n+1)-a(i,n,n+1)*b(n,n+1);
}

float x(int i){
  float d;
  d=b(i,i+1);
  for(int j=0;j<i;j++)d-=a(i,j,i+1)*X[j];
  return d/a(i,i,i+1);	// a(i,i,i+1)==0 => unsolvable
}

void solve(){ 
  X[0]=x(0);  X[1]=x(1);   X[2]=x(2);   X[3]=x(3); 
}

Он дейстивтельно не зависит от вида матрицы, линейная зависимость легко проявляет себя в единственном делении, отмеченном коментарием. Увы, по скорости он уступает даже методу Гаусса. Я использую его там, где надежность важнее скорости.

Примечание: в вышеприведенном тексте есть одна большая ошибка. Этот алгоритм будет надежным только при нахождении первого корня. Остальные находятся на основе уже найденных решений. Никакой проверки на линейную зависимость нахождение делителя не дает. Для действительно надежной работы надо все корни находить по принципу нахождения первого. Тогда задача сведется к решению СЛАУ по правилу Крамера.


Джазовое программирование

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

В программировании такой подход тоже себя оправдал. Если время на отладку какого-нибудь фрагмента кода превышает время его написания — надо просто его переписать с нуля. Правда в том, что никто не любит отказываться от собственного кода. Выбросить то, что почти работает, дабы написать то, что, может быть, не заработает вообще - довольно пугающая перспектива. Хотя практика показывает, что каждый следующий дубль обычно оказывается элегантней и надежней предыдущего, древняя программистская мудрость говорит: <работает - не трогай.> Дабы избавить себя от лишних колебаний, я держу у себя в проекте отдельный файл, который не участвует в сборке, но содержит все неудачные дубли. Честно сказать, обращаться к нему приходится довольно редко.

Примечание: на диске Телониуса Монка есть три дубля композиции . Каждый из них является самостоятельным музыкальным произведением.


Программа — программист

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

Основная причина, по которой для маленьких СЛАУ используются большие алгоритмы — это лень. Проще воспользоваться медленным и общим решением, чем писать быстрое для каждого конкретного случая. Но никто же не запрещает написать общий случай, который будет генерировать конкретный код. Я сделал программку, которая именно этим и занимается: SLAESolver. Она генерирует развернутое решение СЛАУ в текстовом виде. Надо просто заполнить матрицу соответствующими буквами из своего кода и нажать кнопку. При этом возможны два решения: быстрое, но ненадежное (так же как и оригинальный метод Гаусса боится нулевых элементов в неподходящих местах) и надежное, но чуть более медленное.

В среднем, расписанное в линию решение СЛАУ для трех уравнений работает в пять раз быстрее, чем реализованное методом Гаусса.


Отладчики на «Перфоленте»

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

Для успешной отладки, необходимо решить систему уравнений с тремя Х. Что за Х происходит? Когда эта Х появляется? И как эту Х убрать? Дабы облегчить решение первых двух вопросов, в рамках проекта «Перфолента» были сделаны такие утилиты как «Шпион», «Радар» и MT-2.

«Шпион» — это смесь профайлера, отладчика и десяти электронных будильников. Принцип работы прост. Окно «Шпиона», состоящее из десяти счетчиков, пяти кнопок и поля для комментариев, может принимать от любого приложения сообщения WM_USER с двумя параметрами. Первый говорит с каким счетчиком, второе — что делать. Второй параметр, как и положено для настоящей хакерской программы, кодирован цифрой. Делать можно такое: засечь время, замерить время, прибавить единицу, вывести число и обнулить. Допустим, окно «Шпиона» запустилось с таким обработчиком: 1573350. При запуске он выводится в заголовок окна и заодно копируется в буфер обмена, потому что руками набивать цифру нормальному человеку должно быть лень. Теперь надо расставить в отлаживаемом коде строчки навроде таких:

HWND sn_hw=(HWND1573350);
// обнулит первый счетчик;
SendMessage(sn_hw,WM_USER,1,0); 

// начнет считать время для 1-го счетчика;
SendMessage(sn_hw,WM_USER,1,1); 

// прибавит ко 2-му счетчику единицу;
SendMessage(sn_hw,WM_USER,2,3); 

// выведет в 3-й счетчик число 1234;
SendMessage(sn_hw,WM_USER,30,1234); 

// да, там должно быть 40. В 4-й - 5;
SendMessage(sn_hw,WM_USER,40,5); 

// подсчитает время для 1-го счетчика.
SendMessage(sn_hw,WM_USER,1,2); 

В итоге у нас в первом счетчике окажется время, которое ушло на исполнение четвертой, пятой и шестой строчки примера плюс время на вызов самих процедур SendMessage. Во втором счетчике — количество раз, которое пример запускался. В третьей — 1234, и в четветрой — 5. В зависимости от фантазии программиста, при помощи «Шпиона» можно осуществлять профайлинг реального времени, мониторинг переменных, контроль вызовов функций, а также позравление с днем рождения. Для последнего народные умельцы придумали использовать заголовок «Шпиона»:

SetWindowText((HWND)1573350,«С днем рожденья!»)

Рассмотрим следующий пример.

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

Когда происходит: дабы выяснить этот вопрос потребовался «Шпион». В процедуре обработки и отрисовки сцены были расставлены SendMessage так, чтобы постоянно замерять общее время работы сцены в нулевом счетчике, время работы ее компонентов в остальных. Оказалось, что в момент запирания большая часть нагрузки припадала на систему частиц. Расставлены SendMessage в процедуре вывода системы частиц так, чтобы было видно, сколько раз во фрейм вызывается инициация эмиттера. Оказалось, что при нормальной работе редко чаще одного, в момент запирания — пять-десять. Система частиц нагружается в момент отрисовки взрыва корабля. Были расставлены SendMessage в процедуре обработки поведения кораблей, дабы вывести их параметры. Оказалось, что в большинстве случаев, триггер поведения был выставлен на «атаку и преследование».

Что происходит на самом деле: ошибка в логике кораблей. Когда на исстребитель наводятся два и больше вражеских корабля, он начинает убегать в сторону ближайшего своего сокомандника. Сокомандник, видя, что на него навелись, но не понимая, кто, начинает контратаку. Когда на исстребитель нападают два и больше вражеских корабля… В общем, собрав всех товарищей по комманде в одну ораву у себя на хвосте, исстребитель не находя больше сокоммандников, вылетает за край сцены и убивает себя об невидимую сцену. Дым, исркы и слезы от обиды на предателей перегружают процессор.

У «Шпиона» есть одно неприятное свойство. Время, уходящее на отрисовку самих циферок в счетчиках плюсуется к измеряемому. В итоге, при попытке замерить маленькие кусочки кода, он попросту врет. Этот эффект можно уменьшить, убрав галочку на «Realtime print». Но результаты проверять придется раз в несколько секунд.

«Радар» — это аналог «Шпиона», но с OpenGL окном. Он нужен для отрисовки точек на отдельном экране. Пространственное воображение — штука хорошая, но когда одна флотилия идет бить другую армаду с помошью плазмометов, вооражение рисует что угодно, но только не множество точек коллижна.

Принцип работы тот же, что и у «Шпиона». Только вместо счетчиков, список точек. Вот пример, выводящий на экран две точки каждый фрейм:

HWND a=(HWND)5506184;

// отключение отрисовки, чтобы не мигало;
SendMessage(a,WM_USER,’D’,0); 

// очистка списка точек, обнуление счетчика;
SendMessage(a,WM_USER,0,0); 

SendMessage(a,WM_USER,’X’,10); // x;
SendMessage(a,WM_USER,’Y’,10); // y;
SendMessage(a,WM_USER,’Z’,10); // z;

// красная компонента цвета точки;
SendMessage(a,WM_USER,’R’,255);
SendMessage(a,WM_USER,’G’,128); // зеленая;
SendMessage(a,WM_USER,’B’,255); // синяя;

// добавление точки, счетчик плюс один;
SendMessage(a,WM_USER,1,0); 

// вторая точка пошла;
SendMessage(a,WM_USER,’X’,20); 
SendMessage(a,WM_USER,’Y’,10);
SendMessage(a,WM_USER,’Z’,10);

// а остальные компоненты не поменяются;
SendMessage(a,WM_USER,’R’,128); 
SendMessage(a,WM_USER,1,0);

// центрирование по 0-й точке;
SendMessage(a,WM_USER,’C’,0); 

// центрирование координат по 1-й точке;
SendMessage(a,WM_USER,’P’,1); 

// масштабирование координат;
SendMessage(a,WM_USER,’S’,-1); 

// включение отрисовки. Теперь не помигает.
SendMessage(a,WM_USER,’D’,1); 

Изначально инструмент был нужен, чтобы работать вместе со встроенным отладчиком VC++ 6.0. Отладчик этот, обладая добрым нравом, не позволял в момент остановки программы, отображать ее GL-ное окно. Потому пришлось использовать чужое. Если надо трассировать программу в отладчике и одновременно смотреть на результат в окне «Радара», отключать/включать отрисовку не надо.

Пример номер два:

Что происходит: космический корабль вязнет в астероиде.

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

Что происходит на самом деле: корабль врезается в астероид, отталкивается, теряя часть скорости. В этот момент не происходит проверки на предмет того, влетает ли корабль в астероид, или вылетает. Получается, что если корабль не успел отлететь достаточно далеко, он опять врезается в астероид, уже изнутри, теряет часть скорости и к тому же отталкивается вовнутрь. Там от врезается в астероид… На десятой итерации скорость уже стремится к нулю. Воистину, чем круче трактор — тем дальше ездовать за трактором.

«Радар» — программка довольно молодая. В будущем планируется добавить туда отрисовку линий, треугольников, поверхностей, прикрутить шейдера, скелетную анимацию и искусственный интеллект. На случай, если отлаживаемый движок издохнет, у нас всегда будет запасной вариант.

МТ-2 расшифровывается как «экранный мультиметр версии 1.8». Но функциональность у него, тем не менее, вполне адекватная. Он позволяет измерять цвет и позицию точки на экране, обработчик любого окна, положение окна, размер окна. Он даже умеет таскать текст из сообщений об ошибках. Удобно, если ведется какой-то учет таковых.

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

Утилиты с «Перфоленты» не лучше «нормальных» средств для отладки. Они меньше, проще, понятней, предсказуемей, быстрее и надежней, — но не лучше. В них не хватает профессиональскости, лучшести и умности. Каким-то чудом, они ухитряются делать жизнь программиста проще, что недопустимо для профессионального программного обеспечения. И все таки она вертится.






 
Содержание
1. Псевдографика в HTML
2. Сетевая мат. лексика
3. Любимый алгоритм сортировки
4. Забытое решение СЛАУ
5. Джазовое программирование
6. Программа — программист
7. Отладчики на «Перфоленте»
  1. «Шпион»
  2. «Радар»
  3. MT-2
Продолжение следует.
Сайт управляется системой uCoz