Архив за Ноябрь 2009

В конце лета мне надо было оплатить участие в ISDEF. ShareIt (привет!) мою карточку не принял, поэтому пришлось заполнить квитанцию, отстоять очередь и оплатить в Сбербанке.

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

Этаж, на котором обрабатывают квитанции просто потрясает: длинный коридор, заваленный коробками с квитанциями. Коробки везде, даже на шкафах.

sberbank.jpeg

(Это самая незахламленная часть коридора)

В комнате, куда мне надо было обратиться, стояли несколько столов, на них — огромные стопки бумаг. Работницы (работников не было) их перебирали и все время куда-то звонили. На компьютерах с CRT-дисплеями работали синеэкранные желтобуквенные банковские программы. Начальница отдела попросила написать заявление о возврате денег. После этого она ходила в два разных места за подписями (спасибо ей, что мне не пришлось этого делать). Потом дала мне бумажку, на котором был написан номер счета. Я пошел вниз, отстоял сбербанковскую очередь (с электронной системой вызова по номеркам!), сунул в окошко бумажку и мне вернули деньги.

Удивительно, как все это работает и сколько нужно людей, чтобы обработать эти бумажные квитанции. А сколько нужно места, чтобы их хранить!

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

* * *

Чтобы пройти техосмотр, нужно оплатить две квитанции — собственно, за техосмотр и госпошлину. Мы живем в XXI веке, поэтому можно не заполнять квитанции и не ехать в Сбербанк. Есть современный способ. По карточке? Через интернет? Ха-ха-ха. У нас это не работает, ГИБДД нужны бумажки, чтобы создать кучу рабочих мест для их обработки.

Но так как в Россия — страна workaround’ов, в автосалоне рядом с ГИБДД стоит большой автомат. Вы берете две квитанции (реквизиты и тип платежа уже заполнены), вписываете фамилию, имя, отчество и сумму. Суете их по очереди в автомат. Он их сканирует. Сумму не распознает, вводите ее сами. Суете наличные. Автомат выплевывает копии квитанций с пропечатанной информацией о том, что платеж принят (как в Сбербанке).

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

Это электронная Россия.

Google, миллионы людей смотрят на это прямо сейчас:

Screen shot 2009-11-25 at 6.25.41.png

“Открыть доступ” означает… поделиться ссылкой.

Screen shot 2009-11-25 at 6.27.09.png

Тупое копирование переводов слова “share” из Google Docs — это то, что у вас называется локализацией?

Исправьте!

Обновление: Похоже, меня услышали! Эту опцию вообще убрали:

Screen shot 2009-11-27 at 17.55.03.png

Не только с русскоязычной полоски, но и вообще.

Обновление 2: Нет, это был какой-то глюк. “Открыть доступ” все еще там.

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

Во-первых, Fossil не добавляет в репозитории символьные ссылки — он следует по ним и добавляет файлы. У меня в репозиториях лежат скомпилированные фреймворки, а они очень хорошо пользуются ссылками и устроены вот так:

sparkle-framework.png

Как видите, Headers, Resources вверху ссылаются на A/Headers и A/Resources, плюс еще Versions/Current ссылается на A/, таким образом, если по всем ссылкам пойти, Versions/A/Sparkle.h и остальные файлы будут добавлены три раза. Соответственно, если добавить фреймворк в Fossil и сделать checkout, мы получим вот такую картину:

sparkle-framework-fossil.png

Хотя это и валидный фреймворк, это ужасно (заметьте, что также пропал исполняемый атрибут у бинарника — Fossil не запоминает атрибуты).

Если мне будет не лень, я может быть когда-нибудь модифицирую Fossil, чтобы он знал о ссылках и помнил атрибуты. Но тогда придется немного изменить формат репозитория, который “is kept simple so that it can endure in useful form for decades or centuries”.

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

Сейчас у меня вот такой проект:

$ tree
...
251 directories, 2178 files

Если пройтись по ссылкам, что делает Fossil, то файлов окажется еще больше:

$ tree -l
...
317 directories, 2869 files

(Для любопытствующих — нет, это не все мои файлы; в основном это исходники фреймворков, используемых в проекте. Если вы не храните их в репозитории проекта, то совершаете ошибку — через 5 лет, захотев скомпилировать код, вы задолбаетесь искать все зависимости по интернету. Горьким опытом научен.)

Дальше буду сравнивать с эталоном — Git. Добавляем файлы в чистый репозиторий:

Fossil:

$ time fossil add .
...
real    0m0.971s
user    0m0.471s
sys 0m0.191s

$ time fossil commit -m "Initial commit"
New_Version: f71b1fc6112801d8082e0c0e609d455e6a9f899a

real    0m8.639s
user    0m5.788s
sys 0m0.943s

Git:

$ time git add .

real    0m3.780s
user    0m1.430s
sys 0m0.961s

$ time git commit -m "Initial commit"
...
real    0m1.096s
user    0m0.342s
sys 0m0.289s

Итого, Fossil ~ 9.5 секунд, Git ~ 4.7 секунды. Время первого коммита не так важно — как много первых коммитов вы делаете во время работы с проектом? :)

Посмотрим, есть ли изменения (их нет):

$ time fossil changes

real    0m0.824s
user    0m0.405s
sys 0m0.183s


$ time git status
# On branch master
nothing to commit (working directory clean)

real    0m0.307s
user    0m0.038s
sys 0m0.050s

Три десятых секунды у Git против 0.8 у Fossil. Вот эта разница уже существенна. Секундная задержка, хоть и кажется маленькой, раздражает. С горячим кэшем, конечно, задержка уже меньше (если повторить те же операции):

$ time fossil changes

real    0m0.410s
user    0m0.258s
sys 0m0.151s

$ time git status
# On branch master
nothing to commit (working directory clean)

real    0m0.084s
user    0m0.036s
sys 0m0.043s

Но разница очевидна: полсекунды против “ничего”.

Попробуем изменить файлик

$ echo "// Test" >> main.m

и сделать коммит:

$ time fossil commit -m "Edited main.m"
New_Version: 34721d9dca256ad55bbde601e1844c1fd874c8d0

real    0m2.987s
user    0m2.285s
sys 0m0.581s

$ time git commit -a -m "Edited main.m"
[master 5de00cc] Edited main.m
 1 files changed, 1 insertions(+), 0 deletions(-)

real    0m0.193s
user    0m0.049s
sys 0m0.089s

Три секунды у Fossil против двух десятых у Git!

Если вы не верите, что трехсекундный коммит может раздражать в ходе разработки, то подумайте еще раз: я оторвался от программирования, чтобы сравнить производительность Fossil и Git и написать эту заметку. Стал бы я это делать, если бы меня это не раздражало? :) (Да и Git, собственно, зародился частично из-за того, что monotone, который, кстати тоже построен на SQLite, оказался медленным).

Я не знаю, почему именно этот проект заставил меня задуматься об отказе от Fossil. В SQLite, который трекается в Fossil, тоже немало файлов, и Fossil работает с ним быстрее. Возможно дело в большом количестве вложенных директорий, а может в том, что Fossil читает ссылки… Учитывайте так же, что я программирую на MacBook Air (который большой тормоз, а не компьютер — ATA диск с 4200 об/мин).

Что делать? Для Git Торвальдс написал крутейшую оптимизированную версию SHA-1, а Fossil использует “reference” версию. Я заменил эту версию имплементацией из Git — производительность выросла (fossil changes работает в два раза быстрее), но все равно недостаточно. На самом деле, бенчмарки вверху как раз сделаны с помощью этой “оптимизированной” версии.

Надо заметить, что Git устроен по-другому в плане коммитов: сначала надо добавлять в индекс изменившийся файл, а потом делать коммит, но даже “git add .” картины не меняет — Git находит изменившиеся файлы и делает коммит намного быстрее.

Еще одно важное отличие — Fossil делает дельта + deflate-компрессию над всеми изменившимися файлами, а git просто делает deflate; пакование дельтой у него — отдельный процесс. Но не думаю, что изменение одной строчки одного маленького файла на примере вверху, и, соответственно, дельта-сжатие его, сильно влияет на общую картину.

Посмотрю, что еще можно сделать, но производительность Fossil разочаровывает.

Кстати, про Mercial. Когда я пользовался Git, у меня в .bash_profile была интересная штука — при заходе в директории вызывалось:

git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'

и после имени директории показывался бранч:

~/Projects/Foo(master) $

Когда я перешел на Mercurial, я заменил эту строчку на показ бранча hg:

hg id -bn 2> /dev/null | sed -e 's/\(.*\) \(.*\)/(\2:\1)/'

Потом забыл про это и думал — какого фига у меня bash так долго в папки заходит? hg же на питоне сделан. У меня Python запускается при холодном кэше полторы секунды. Неудивительно. Стоит ли говорить, что коммиты в hg меня тоже раздражали? :)

Интервью со мной на Open Voice про Go и gotweet (на английском).

fossil-ssl.pngFossil, моя новая любимая система контроля версий, работает по протоколу HTTP. Аутентификация пользователя там сделана секьюрно — логин и пароль никогда не передаются в открытом виде. А вот все остальное идет как есть. Для опенсорса это нормально, а вот свои гениальные проприетарные исходники страшновато в plain text передавать, поэтому я написал поддержку HTTPS.

Поддержки пока нет в официальных бинарниках (Fossil — штука независимая, и добавлять еще зависимость от libssl как-то не особо хочется). Поэтому надо скачать исходники с официального репозитория Fossil из бранча “ssl”. По умолчанию этот вариант собирается без SSL; чтобы собрать с SSL, надо запустить make так:

$ FOSSIL_ENABLE_SSL=1 make

Стоит заметить, что автор Fossil (и SQLite), D. Richard Hipp, пишет очень понятный код — разобраться в исходниках не составило труда, несмотря на то, что они на Си :)

Спасибо всем, кто мне помогал!

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

Если надо для Линукса, дайте знать в твиттере.

После просмотра фильма “District 9”.

binary_commands.png

Мотивационный постер для программистов.

CA9F5C25-6CB3-4B23-BFBD-83AA1004F25F.jpg А вот и более-менее полноценный консольный клиент для твиттера, написанный на Go прилетел/прибежал — gotweet.

Два дня возился, пытался понять как что устроено в библиотеках Go. Обнаружил отсутствие поддержки basic authentication в http, пришлось написать самому.

Нашел баг в парсинге JSON, который разработчики Go исправили меньше чем за час.

Основные сложности были с io — долго пытался понять как им пользоваться ([]byte vs “bytes” package? io.Writer/Reader?).

Кросс-компиляция рулит: оба бинарника для Mac OS X и Linux собраны на Маке.

Качайте исходники или бинарники: http://codingrobots.org/p/gotweet/

PS Впечатления о языке опять будут позже, устал я от него :-)

logo.png Всем известно, что твиттер-клиенты — это новые “Hello world” в программировании.

Google вчера выпустил очень интересный язык программирования Go.

Я написал на нем мини-клиент для твиттера, который просто выводит последние записи из public timeline:

package main

import (
    "http";
    "io";
    "fmt";
    "json";
    "os";
    "regexp"
)

type User struct {
    Screen_name string;
}

type Tweet struct {
    Text string;
    Source string;
    User User;
}

type Timeline struct {
    Tweets []Tweet;
}

func main() {
    r, _, err := http.Get("http://twitter.com/statuses/public_timeline.json");

    if err != nil {
        fmt.Printf("Connection error: %s\n", err.String());
        os.Exit(1);
    }

    if r.StatusCode != 200 {
        fmt.Printf("Twitter returned: %s\n", r.Status);
        os.Exit(1);
    }

    b, _  := io.ReadAll(r.Body);
    r.Body.Close();

    // Twitter sends malformed JSON, with top-level array. 
    // Workaround: put it under 'tweets'.
    s := `{"tweets" : ` + string(b) + `}`;

    var timeline Timeline;
    json.Unmarshal(s, &timeline);
    re, _ := regexp.Compile("<a[^>]*>(.*)</a>");
    for _, t := range timeline.Tweets {
        // Source may be a link: <a href="...">source</a>
        // Extract text of the link with regexp.
        matches := re.MatchStrings(t.Source);
        if matches != nil && len(matches) > 0 {
            t.Source = matches[1];
        }

        if t.Text != "" {
            fmt.Printf("%v: %v (%v)\n\n", t.User.Screen_name,
                t.Text, t.Source)
        }
    }
}

Позже расскажу о своих впечатлениях от Go.

Обновление: добавил вывод источника (web, Tweetie, etc.), чтобы показать работу с регулярными выражениями.

Daniel Jalkut и компания организовали One Finger Discount, чтобы показать палец MacHeist. Смысл: используйте купон “OneFingerDiscount”, чтобы получить 20% скидку на перечисленный софт до 13 ноября.

Coding Robots’овский Mémoires тоже участвует.

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

FC9DB7D2-3F8D-49B5-A53E-5E0B7A9716E7.jpg

Bombardier, биттер сваренный на Wells & Young, я пробовал давно, но он мне не понравился (аж недопил бутылку! видимо, тогда я не врубался ни во что кроме лагеров). Однако, решил попробовать еще раз и — тадам! — понравилось. Алкоголь 4.3%, плотность 13%. В составе, кроме обычных ингредиентов: карамель (E150C) и загуститель со страшным названием “Пропиленгликольальгинат” (E405).

Я не дегустатор, но уловил фруктовый вкус. Посвежее и покислее, чем Newcastle. В целом, очень вкусно и приятно пьется.

Кстати, на русской этикетке не пишут про E405 (по-моему, у нас не обязательно указывать все ингредиенты). С другой стороны, фанаты натуральной пищи обычно орут про ДОБАВКИ E###, химия-химия, …, а сами не знают, что это такое.

С третьей стороны, на банке пишут:

Wells Bombardier uses more malt per pint than other premium bitters, and the English ‘Ale’ and ‘Crystal’ malts are carefully crushed, not ground. This delivers the rich flavour and deep copper colour.

Ну про цвет-то явно врут! Цвет делается E150C, а не бла-бла-блой.

Тем не менее, эль вкусный.

PS На BeerBottle.ru есть обзор Bowman Stout от той же пивоварни — читайте там про историю Wells and Young. Кстати, Bowman Stout мне не понравился, но я просто не люблю стауты, в отличие от автора блога (которому тоже Bowman не понравился :).

Открыл сайтик с проектами Coding Robots с открытыми исходниками. В основном, это полезные штучки для Mac-разработчиков: http://codingrobots.org.