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

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

Хотя это и валидный фреймворк, это ужасно (заметьте, что также пропал исполняемый атрибут у бинарника — 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 меня тоже раздражали? :)
—
22.11.2009