четверг, 12 марта 2009 г.

Похоже, что SObjectizer 4.4.0 будет поддерживать только TCP/IP

Приступил к разработке седьмой бета-версии SObjectizer 4.4.0. С таким прицелом, чтобы сделать ее сразу релиз-кандидатом. И, если по прошествии трех-четырех месяцев активной эксплуатации не будет выявлено серьезных проблем – объявить ее финальной версией 4.4.0.

Одна из целей beta7 – поддержка еще одного вида транспорта в SObjectizer. Хотел реализовать взаимодействие SObjectizer-процессов через разделяемую память. Благо, видел в ACE средства для ее поддержки. Но не тут-то было. Маленький пушной зверек, как водится, подкрался незаметно.

В ACE действительно есть средства для работы с разделяемой памятью. Низкоуровневые и высокоуровневые. Высокоуровневые (т.к. ACE_MEM_Stream, ACE_MEM_Connector, ACE_MEM_Acceptor) реализованы так, чтобы вписываться в стандартную ACE-овскую архитектуру реакторов. И, поскольку в SObjectizer 4.4 транспортный слой как раз ориентирован на ректоры и Event_Handler-ы, то я решил воспользоваться именно высокоуровневыми средствами.

Разобраться с механизмом работы ACE_MEM_* классов оказалось не просто. Т.к. никакой внятной документации по ним нет, за исключением небольших Doxygen-комментариев. Так что пришлось лазить прямо по исходникам. Здесь в очередной раз хочется сказать, что OpenSource это есть очень хорошо и правильно. При наличии исходников можно понять все. Тем более, что качество кода в ACE довольно паршивенькое, но благо без мозгодробительных трех-этажно-шаблонных конструкций. Разобраться что к чему удалось. И вот, что выяснилось…

Оказывается, у ACE реализован свой транспорт на основе разделяемой памяти. Транспорт этот может быть двух видов: с передачей уведомлений через TCP/IP сокет (режим ACE_MEM_IO::Reactive) или через примитивы синхронизации (режим ACE_MEM_IO::MT). Но в любом случае для канала на основе разделяемой памяти требуется TCP/IP сокет. Насколько я понял, как вся эта кухня работает так:

1. Серверная сторона создает серверный TCP/IP сокет и ожидает подключение клиентов. Когда клиент подключается, серверная сторона через TCP/IP сокет договаривается с клиентом о способе коммуникации (Reactive или MT). После чего создается отображаемый в память файл и имя этого файла передается через тот же сокет клиенту. Клиент получает это имя и открывает данный файл.

2a. Если работа идет в режиме ACE_MEM_IO::Reactive, то о каждой операции записи в разделяемую память делается нотификация удаленной стороны через запись уведомления в TCP/IP сокет. Т.е., при записи в ACE_MEM_Stream данные копируются в разделяемую память, а уведомление о них пишется в сокет.

2b. Если работа идет в режиме ACE_MEM_IO::MT, то используются примитивы синхронизации ОС (вроде бы semaphore и condition variable) для уведомления удаленной стороны. Т.е., при записи в ACE_MEM_Stream данные копируются в разделяемую память и взводится condition variable. Если удаленная сторона спала на этом condition variable, то она проснется.

Подлость в том, что в режиме ACE_MEM_IO::Reactive можно повесить ACE_MEM_Stream на реактор. И реактор будет уведомлять о поступлении данных. Т.е. поддерживается та схема работы, на которую был ориентирован транспортный слой в SObjectizer 4.4.0. Но при этом скорость работы через разделяемую память оказывается (на мелких порциях данных) даже ниже, чем при работе через сокеты. А если взять режим ACE_MEM_IO::MT, то для получения входящих данных нужно висеть на recv() постоянно. Т.е. нужно выделять отдельную нить, которая будет читать входящие данные. А потом еще и управлять этой нитью как-то. Причем хотелось бы, чтобы данная нить могла прослушивать сразу несколько каналов (как это происходит в ACE_Select_Reactor-е с сокетами). И если под Windows еще можно было бы что-то придумать с WaitForMultipleObjects (или ACE_WFMO_Reactor), то что делать под Unix-ами я не очень представляю.

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

Итак, в документации к ACE сказано, что ACE_MEM_Stream за один раз не может передавать больше, чем было первоначально выделено разделяемой памяти. Ну не может, так не может. Но что будет, если попробовать это сделать? Операция recv() возвращает, как положено, –1. А затем программа аварийно завершается. Выяснилось, что деструктор ACE_MEM_Stream пытается записать что-то в канал. Попытка записи приводит к обращению по некорректному указателю. Но откуда этот указатель берется?

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

          else if (currp == this->cb_ptr_->freep_)
            {
              // We've wrapped around freelist without finding a
              // block.  Therefore, we need to ask the memory pool for
              // a new chunk of bytes.

              size_t chunk_bytes = 0;

              currp = (MALLOC_HEADER *)
                this->memory_pool_.acquire (nunits * sizeof (MALLOC_HEADER),
                                            chunk_bytes);
              void *remap_addr = this->memory_pool_.base_addr ();
              if (remap_addr != 0)
                this->cb_ptr_ = (ACE_CB *) remap_addr;

Управление попадает в ACE_MMAP_Memory_Pool::acquire(), оттуда в ACE_MMAP_Memory_Pool::map_file(). И одним из первых действий в map_file оказывается:

  // Unmap the existing mapping.
  this->mmap_.unmap ();

т.е. происходит отмена отображения части файла в адресное пространство процесса (соответственно, все адреса, которые были определены в данном отображении, становятся “повисшими”). Нужно это, по-видимому, для того, чтобы затем отобразить в адресное пространство кусок файла большего размера. Но эта попытка завершается неудачно. И, в результате, ACE_MMAP_Memory_Pool::acquire возвращает 0.

Однако, самое важно то, что в ACE_Malloc_T атрибут cb_ptr_ указывает как раз на отображенный в адресное пространство процесса фрагмент файла. Но данного фрагмента уже нет, т.к. было выполнено обращение к unmap()! Т.е. после возврата из acquire() значение this->cb_ptr_ уже содержит мусор!

Похоже, что разработчики ACE расчитывали на то, что после acquire() значение cb_ptr_ станет некорректным. Именно поэтому в коде ACE_Malloc_T::shared_malloc() стоит проверочный код:

              void *remap_addr = this->memory_pool_.base_addr ();
              if (remap_addr != 0)
                this->cb_ptr_ = (ACE_CB *) remap_addr;

Но этот код рассчитан на то, что remap_addr не будет нулевым. Т.е., что map_file() не завершается неудачно. А тут завершается. В результате в this->cp_ptr_ так и остается мусор. И на этот мусор мы натыкаемся, когда деструктор ACE_MEM_Stream пытается что-то записать в канал. Как вполне естественное следствие – крах приложения.

Вот такие вот пироги. Я лично убежден, что раз уж recv() возвращает –1, то ничего больше в программе ломаться не должно. Но в случае с ACE это не так.

По сумме всех вышеизложенных факторов я решил не делать в седьмой бете поддержку транспорта на основе разделяемой памяти. Поскольку высокоуровневый механизм ACE для разделяемой памяти оказался медленным (в случае ACE_MEM_IO::Reactive) или неудобным в использовании (в случае ACE_MEM_IO::MT), да еще и глючным. А делать какой-то свой механизм с нуля не очень хочется. Жалко времени, честно говоря. Есть еще важные вещи, которые хотелось бы включить в SObjectizer 4.4.0 и освободить время и ресурсы на разработку SObjectizer-5.

Такие дела. Так что останется SObjectizer 4.4.0 только с TCP/IP транспортом. По крайней мере пока не возникнет очень настоятельной необходимости в поддержке чего-то другого.

понедельник, 9 марта 2009 г.

Об интуитивности императивного и функционального программирования

Данная тема навеяна очередным RSDN-новским флеймом по поводу функционального программирования (ФП). Толчком стало утверждение, что в 1980-е годы объектно-ориентированное программирование (ООП) с трудом завоевывало себе место под солнцем. Не знаю, что происходило на Западе в 1980-е, но в 1992-м я уже программировал на C++ с объектами, даже не подозревая, что использую ООП. Об этом я узнал несколько позже, наверное, в 1994-м. Тогда же, может чуть раньше, я стал понимать, что переход к ООП действительно требует некоторого изменения способа мышления. Но у меня самого это изменение прошло незаметно и безболезненно. Чего не происходит по отношению к ФП. Почему же?

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

Более того, воспитание человека идет в том ключе, что любое его действие неизбежно ведет к последствиям. Поэтому наши действия должны быть продуманы и взвешены. Чтобы не было мучительно больно и т.д.

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

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

Итак, все наше существование показывает нам, что мир вокруг изменяем и, местами, структурирован.

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

Да и само объяснение принципов работы компьютеров, услышанное когда-то в школе, вполне согласовывалось с обычным ощущением изменяемости нашего мира. Мол, есть память, состоящая из ячеек. Есть процессор, есть текущая позиция в памяти. Процессор берет значение из ячейки, текущая позиция сдвигается... И т.д., и т.п. Как-то без проблем стало понятно, почему память конечна, и почему целое число может содержать значения только в каком-то диапазоне. Реальный мир, ничего с ним не поделаешь.

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

Благо, в университете вначале обучение велось на Turbo Pascal, в котором модули были на уровне языка (помнится, они были слизаны с Modula-2). Поэтому вхождение в структурное и модульное программирование произошло быстро и незаметно.

Затем я переключился на C. Хотя сам C мне не нравился. После Turbo Pascal он был какой-то сильно замороченный, да и компилировался на порядки дольше. А осенью 1991-го я раздобыл у знакомого книгу “Язык программирования C++” (первое издание) в электронном виде. По ходу ее первого чтения я даже не отдавал себе отчета о том, что это не C, а другой язык (вероятно, я просто не распечатал “Введение” из книги). На полном серьезе: я полагал, что книга описывает просто новую версию языка C. Поэтому был очень удивлен, когда Turbo C 2.0 оказывался компилировать мои программы с оператором new :)

Так вот, о переходе к ООП. После Паскаля в C мне очень не хватало такой простой и нужной вещи, как множества. В Паскале можно было объявить, например, множество символов и проверять, есть ли там какой-то символ. В C множеств не было, что вызывало у меня жуткий дискомфорт. Зато в Паскале нельзя было написать собственную функцию, получающую переменное число аргументов. Стандартные Write и WriteLn могут получать разное количество аргументов, а мои собственные функции - нет. Т.е. Паскаль не позволял программисту достигать того, что могли сделать разработчики компилятора Паскаль. А в книге я читаю, как в C++ средствами самого языка можно организовать множество. И что это множество будет вести себя точно так же, как и “зашитые” в язык вещи (вроде int). Вот тут-то я и попался на крючок C++. Этот язык давал пользователям языка те же возможности, что и своим создателям.

Само понятие класса в C++ для меня стало аналогом понятия unit из Turbo Pascal. В общем, такое же средство обеспечения модульности, только чуть в других масштабах и с несколько другими возможностями. Кстати, до сих пор очень жалко, что в C++ нет таких модулей, как в Turbo Pascal :(

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

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

В отличие от функционального программирования, которое строится на абстракциях. А абстракции, это такая хитрая штука… Абстракции на то и абстракции, что их сложно объяснить на пальцах, на примерах того, что можно увидеть за окном. В моем случае, проблемы с математикой начались с возникновения в школьной программе пределов, дифференциалов и интегралов. Предел - это абстракция. Для меня предел - это пустой звук. Я не могу себе представить предел ни в виде какого-то предмета, ни в виде рисунка. Поскольку я не могу понять, что это, то я не могу представить себе ни зачем он нужен, ни как его использовать. Выучить вид пределов и операции над ними можно. Но понимания-то нет. А значит, нет и использования.

Так вот, возвращаясь к ФП. Программа, состоящая из функций. А функции не производят побочных эффектов. Мы запускаем функцию несколько раз и получаем один и тот же эффект. Поскольку вокруг ничего не менялось. Т.е. мы нарисовали линию на бумаге. Стерли ее. И нарисовали линию еще раз. Точно такую же. Абсолютно точно такую же. Но ведь это же противоречит тому, что мы познавали в различных проявлениях с самого детства - любое действие изменяет мир вокруг нас. Итак, первое противоречие с практическим опытом: функция - это абстракция, которую сложно объяснить на пальцах.

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

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

Что ж, пора закругляться.

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

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

Собственно, в разгорающемся сейчас интересе к ФП меня больше всего волнует вопрос о том, компенсирует ли ФП затраты, понесенные на его изучение и применение. Ответа у меня нет, но есть весьма скептическое отношение к ФП.

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

суббота, 7 марта 2009 г.

Наткнулся на прошлогоднее интервью с Дональдом Кнутом

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

Об OpenSource и закрытом ПО:

Успех программ с открытыми текстами - это, вероятно, единственное явление в компьютерной области, которому я в последние несколько десятилетий не удивляюсь…
…Тем не менее, я думаю, что небольшая часть программ, таких как Adobe Photoshop, будет всегда превосходить конкурентов из категории open source, таких как Gimp, по неизвестной мне причине.

Кнут развенчал в этом интервью легенду о том, как он выиграл соревнования программистов с помощью программы, которая заработала после первой же компиляции:

Все участники соревнования, кроме меня, работали в его лаборатории искусственного интеллекта, которая располагалась на холмах выше Стэнфорда, и использовали систему разделения времени WAITS. Я сидел внизу, на основной территории университета, и единственным доступным для меня компьютером был мейнфрейм, для которого я должен был пробивать перфокарты и отдавать их на обработку в пакетном режиме. Я пользовался системой Вирта ALGOL W (предшественником Pascal). Моя программа не заработала с первого раза, но, к счастью, я смог воспользоваться замечательной оффлайновой системой отладки Эдда Саттертвейта (Ed Satterthwaite), так что мне понадобилось всего два захода. Тем временем, ребята, пользовавшиеся WAITS, не смогли получить достаточного машинного времени, поскольку их машина была перегружена. (Я думаю, что программист, занявший второе место, который использовал этот “современный” подход, пришел к финишу почти через час после того, как я представил работающую программу, полученную с применением старомодных методов.) Это соревнование не было справедливым.

По поводу перехода к многоядерным архитектурам:

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

В этих словах я нашел подтверждение своим опасениям о том, что многопоточное программирование грозит превратиться в кошмар.

О том, почему literate programming не получило широкого признания:

Literate programming - это очень личная вещь. Мне оно кажется бесподобным, но это, может быть, потому, что я очень странный человек. У этого подхода имеются десятки тысяч поклонников, но не миллионы…
…Тем не менее, лично для меня literate programming - это наиболее важная вещь, вышедшая из проекта TeX. Этот подход не только позволил мне писать и поддерживать программы быстрее и надежнее, чем когда бы то ни было раньше, и он не только был для меня самым большим источником удовольствия, начиная с 1980-х гг. - он иногда оказывался незаменимым. Некоторые из моих основных программ, такие как метасимулятор MMIX, не могли бы быть написаны с применением любой другой методологии, о которой я когда-либо слышал. Сложность была просто чересчур устрашающей, чтобы с ней можно было справиться на основе моих ограниченных умственных возможностей; без применения literate programming все предприятие потерпело бы полную неудачу.
Если люди действительно обнаружат хорошие способы использования новомодных многоядерных машин, то я думаю, что это сделают те люди, которые повседневно используют literate programming. Literate programming - это то, что требуется для превышения обычного уровня достижений. Но я не считаю разумным навязывание идей кому бы то ни было. Если грамотное программирование - это не ваш стиль, забудьте о нем и делайте то, что вам нравится. Если этот подход не будет нравиться никому, кроме меня, пусть он умрет.

И еще одна цитата на эту тему:

Вероятно, правильно сказал Джон Бентли (Jon Bentley), когда его однажды спросили, почему грамотное программирование не овладело стремительно всем миром. Он заметил, что хорошо программировать умеет небольшая часть населения земного шара, а хорошо писать на естественном языке - другая небольшая часть. По-видимому, мне хотелось, чтобы каждый человек входил и в ту, в и другую группу.

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

О его подходе к работе:

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

Мне кажется, что не зря здесь упомянута большая корзина для мусора :) Чем-то напоминает вот эту историю о китайском художнике.

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

У меня имеется несколько самодельных инструментов, таких как мой собственный спел-чекер для TeX и CWEB внутри Emacs. Я разработал собственный растровый шрифт для использования с Emacs, потому что я терпеть не могу то, как символы ASCII “апостроф” и “левая открывающая кавычка” эволюционировали в независимые символы, не соответствующие друг другу визуально. В Emacs у меня имеются специальные режимы, помогающие мне классифицировать все мои десятки тысяч файлов со статьями и заметками, а также специальные “быстрые клавиши”, наличие которых превращает писание книги в некоторое подобие игры на органе.

О том, какую работоспособность имеет человек в 70(!) лет:

Сейчас я чувствую себя таким же здоровым, как и всегда, с поправкой на то, что мне уже 70 лет. Когда я пишу TAOCP, мои слова текут легко, и я пишу грамотные программы, предшествующие вариантам TAOCP. Я просыпаюсь утром с идеями, которые меня радуют, и некоторые из этих идей нравятся мне и позже, днем, когда я ввожу их в свой компьютер…
…Что касается текущих каталогов в моей машине, в этом году я пока написал 68 разных CWEB-программ. В 2007-м году их было 100, в 2006-м - 90, в 2005-м - 100, в 2004-м - 90 и т.д. Кроме того, в CWEB имеется исключительно удобный механизм <изменения файлов>, с помощью которого я быстро создаю несколько версий и вариаций на одну и ту же тему; пока в 2008-м г. я создал 73 вариации этих 68 тем. (Некоторые из вариаций являются совсем короткими, всего несколько байт; другие имеют размер в 5 Кб и больше. Некоторые CWEB-программы имеют значительный размер, как, например, 55-страничный пакет BDD, работу над которым я завершил в январе.)

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

Он так же раскрывает секрет экономии времени:

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

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

PS. Где-то я читал высказывание Кнута о том, что если бы у него был выбор между двумя равноценными средами разработки, то он лично предпочел бы ту, у которой лучше средства отладки. Буду очень признателен, если кто-нибудь укажет на ее первоисточник.

пятница, 6 марта 2009 г.

Выпустил Mxx_ru 1.4.9

В последнее время развитие Mxx_ru доказывает, что пользователи для программного продукта – это все. Если пользователи есть, то среди них найдутся достаточно активные люди, чтобы высказать автору продукта свои пожелания и заставить его (автора то бишь) воплотить эти пожелания. Тем более, когда пользователи делают значительную часть работы вместо автора :)))

Вот так и произошло с Mxx_ru 1.4.9. Сначала Тимур Найрулин захотел скомпилировать проект CGRU под MacOS X. Затем он выяснил, почему это не получается с Mxx_ru 1.4.8. Затем он разузнал, что нужно указать компилятору/линкеру под MacOS. И мне осталось только вставить все это дело в Mxx_ru в виде нового тулсета gcc_darwin.

Затем Игорь Мирончик сделал патч для Mxx_ru, в котором реализовал поддержку инструмента lupdate из состава Qt4. И мне осталось только накатить этот патч.

За что им от меня большое человеческое спасибо! ;)

В результате, на RubyForge появилась новая версия Mxx_ru. Мне остается только сделать обновления в документации…

среда, 4 марта 2009 г.

SObjectizer и C++: вместе навсегда!

На C++ я программирую уже очень давно. Наверное, года с 1992-го, а может и с 1991-го. Блин, столько не живут :)

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

Через два месяца SObjectizer-4 будет семь (блин, вдуматься – СЕМЬ!) лет. Это достаточно долго. Хотя бы для того, чтобы всерьез заниматься проектированием SObjectizer-5.

Временами желания выбросить C++ и написать SObjectizer-5 “с нуля” совпадают во времени и пространстве. И я начинаю обдумывать, как мог бы выглядеть SObjectizer-5 на другом языке. На D. Или на Eiffel. Или на Scala. Ada. OCaml. C#. Вот в минувшие выходные опять смотрел в сторону Scala…

Но, практически всегда я останавливаюсь на одном вопросе: “А нужен ли будет SObjectizer на этом языке вообще?”

Для JVM-языков (Java и Scala в первую очередь) он вообще не нужен. Во-первых, потому, что для JVM сейчас и так только не ленивый клепает свои actor framework-и. Во-вторых, потому, что в мире JVM почему-то очень силен пиетет перед “правильными” решениями и технологиями. Вот J2EE и EJB – это правильные веши. Остальное – велосипедостроение.

Платформа .NET пока для меня загадка. В каких задачах она широко используется? Будет ли там востребован? Пока не понятно.

Eiffel и Ada сами по себе достаточно маргинальные языки (по крайней мере в нашей стране). Так что даже если SObjectizer-5 окажется восстребованным на Eiffel или Ada, то речь будет идти разве что об единичных применениях. Поскольку вряд ли Eiffel/Ada сами по себе применяются чаще :)

Очень хорошо могло бы быть с D. Поскольку для D сейчас вообще мало чего есть, а интерес он к себе привлекает изрядный. А тут бы мы с SObjectizer-5 подсуетились. Здорово могло бы быть, тем более, что на D приятно программировать. Одна сверхзвуковая скорость компиляции чего стоит! Но… Но пока сам D все еще в стадии формирования. И лично у меня уже нет больших надежд на то, что он когда-нибудь из этой стадии вырастет во что-то стабильное и надежное.

Так что остается C++. Со всеми его прелестями.

Положительным моментом в выборе C++ является то, что для C++ не так уж много проектов, подобных SObjectizer-у. Подтверждение чему я получил буквально вчера.

Вчера я получил письмо от француза (судя по имени и фамилии), который просил помочь в компиляции SObjectizer под Linux-ом. Я офигел. Поскольку мало того, что я не давал каких-либо анонсов в англоязычных изданиях, так еще нигде нет никакой документации по SObjectizer на английском. А тут человек нашел SObjectizer, скачал его, разобрался, что нужны еще ACE, Ruby, RubyGems и Mxx_ru. Установил все это и даже наполовину скомпилировал! Офигеть!!! (Оказалось, что он переводил документацию по SObjectizer с помощью Google Translate).

Когда же я у него спросил, откуда он узнал о SObjectizer, он ответил:

Since a long time, I search C++ toolbox make to create agents (and MAS). The majority of development made under java, but I want to use C++ for the speed execution. It seems that the majority of laboratory teams prefer java. So I try to search also under sourceforge, and I found sobjectizer existence.

Вот оно как. Дела с агентными фрейворками для C++ настолько плохи, что приходится брать инструмент с русскоязычной документацией и изучать его с помощью автоматических переводчиков. Лично я бы никогда не взялся смотреть на инструмент с французкой или португальской документацией.

А посему можно смело ориентировать SObjectizer на C++. Ведь не смотря на все свои недостатки, C++ уже давно достиг состояния immortality. Поэтому на нем пишут сейчас и еще долго будут писать. В том числе и SObjectizer ;)

вторник, 3 марта 2009 г.

Из непонятого: публичные личные телефонные разговоры

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

Меня вообще поражает способность некоторых людей отключаться от окружающего их мира и целиком погружаться в телефонный разговор, совершенно не заботясь о том, что их слушают. В первый раз я столкнулся с этим, когда мне было лет 18 и я месяц просидел в комнате у теток-бухгалтеров на маминой работе. Каждая их них минут по сорок в день тратила на разговоры с подругами/родственниками совершенно не смущаясь ни своих коллег, ни меня. За это время я узнал состав их семей, где, кто и с кем живет (включая породы и клички собак и кошек), кто и чем занимается, и какие события происходили у них в последнее время.

Одно время я думал, что к подобным явлениям предрасположенны, в основном, женщины. Но, как раз мобильные телефоны убедили меня, что здесь царит полное равноправие – наткнуться в маршрутке/автобусе на мужика, десять минут трындящего какую-то бытовую муру в телефон, не сложнее, чем на такого же экземпляра женского пола. Причем, если к публичным телефонным разговорам в транспорте больше склонны дамы лет сорока и старше, то у мужчин ситуация обратная – в основном треплются молодые ребята.

Собственно, что подвигло меня на написание сего опуса?

Во-первых, я искренне не понимаю, как можно не стесняясь, находясь среди посторонних людей (не важно, в транспорте или на работе), вести личные разговоры? Задумываются ли они, например, о своей безопасности? Вот прокричишь на всю маршрутку, что завтра уезжаешь в Куршавель на недельку, а рядом с тобой окажется домушник ;) Проводит тебя до дома, узнает где живешь, а через пару дней навестит твою квартиру… Или, что вероятнее, нервы у невольного слушателя лопнут, и влепит он тебе хук справа в челюсть…Ну, а если не быть параноиком, то почему люди не думают о своих попутчиках? Вам самим вряд ли было бы приятно выслушивать сагу об удачно купленных зимних сапожках, в трех разных изложениях. Так почему же вы ставите в такое положение других людей?

Во-вторых, когда-то я читал книгу по тайм-менеджменту. Кажется, это была “Тайм-драйв. Как успевать жить и работать” Глеба Архангельского. Если мне не изменяет мой склероз, автор дает совет рационально использовать время, которое мы проводим в дороге (в метро, такси, автобусе, маршрутке). В частности, тратить его на необходимые телефонные разговоры. Так вот, хочется обратиться с просьбой к тем, кто последует этой рекомендации: пожалуйста, подумайте о людях, которые оказались рядом с вами. Нервы у меня не железные, могу и хук справа залепить! ;)

Из непонятого: превратим программы в криптограммы?

Определенно, у программистов есть и желание, и способность изобретать нечитаемый синтаксис для своих программных творений. Ранее я об этом уже говорил в юмористической форме. Вчера, читая блог команды разработчиков Maestro в очередной раз в этом убедился. Так, в Maestro для поддержки flow-based подхода изобрели следующие операторы:

==>
-<<
>>-
-<:
&>-

Попробуйте догадаться, какой из них что обозначает. Хотя бы вот в этом примере:

{ a ==> TraceNumber, b ==> TraceNumber } 
   &>- join -<: { GetSum, GetSum } >>- sum;

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

Возможно, причины возникновения таких криптограмм были как-то озвучены Артемием Лебедевым в его Бизнес-линче: “Физики, химики, математики, кибернетики, программисты — все они не в состоянии нарисовать себе приличные логотипы”. Т.е. нет у программистов способностей к графическому дизайну. А, поскольку выбор понятных и простых обозначений для операций – это и есть дизайн, то неудивительно, что что получаются вот такие результаты.

PS. Пользуясь случаем, не могу не бросить еще раз камешек в огород хардкорных функциональных языков. Цитата из статьи про монады:

choose p d1 d2 = Dist {support = s, gen = g, expect = e}
   where
      s = support d1 ++ support d2
      g sg = let (x,sg') = randomR (0.0,1.0) sg in if x < p then gen d1 sg' else gen d2 sg'
      e f = p * expect d1 f + (1-p) * expect d2 f

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

PPS. Да, у меня с дизайном все замечательно. Я без труда придумываю удачные идентификаторы и графические обозначения :)))