воскресенье, 25 августа 2013 г.

Возможности повышения скорости разработки на примере одного приложения

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

Несколько лет назад пришлось резко сменить работу: после почти 10ти лет разработки на  С++ и WinApi разных, больших и не очень, но очень интересных проектов, пришлось перейти в крупную компанию и заняться разработкой сайтов на .Net. Для меня это была довольно резкая смена, поскольку пришлось всё начать практически с нуля. 

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

Быстрый поиск
Мой первый проект содержал сотни мегабайт исходного кода и любая попытка найти хоть какую-то полезную информацию занимала очень много времени. Найти бесплатное готовое решение для поиска с приемлемой скоростью и малым потреблением памяти не получилось, поэтому я решил написать своё приложение на .Net. Честно говоря, после С++ он казался чем-то чрезвычайно медленным и потребляющим нереальное количество памяти. Поэтому для меня эта задача стала весьма интересным вызовом.
За несколько недель был придуман и реализован простейший алгоритм, с предельно простой идеей: для создания индекса, извлекаем из файла все слова и собираем простой словарь, ключ в котором - слово, а значение - коллекция идентификаторов файлов. Хранить полные пути к файлам получалось весьма неэффективно, поэтому путь к файлу разбивался на части и каждой папке присваивался свой идентификатор, в итоге файл идентифицировался именем и набором идентификаторов папок, что позволяло сэкономить память. Поиск изначально работал только по словам, но потом дописал и простой полнотекстовый поиск.
Попутно выяснились новые плюсы наличия хорошего быстрого поиска: теперь когда необходимо было разобраться с какой-то новой технологией, можно было после прочтения документации посмотреть как её принято использовать в проекте, что позволяло избежать лишних велосипедов, а также узнать некоторые интересные особенности использования.

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

Тестирование запросов
Следующей проблемой с которой пришлось столкнуться стала банальная отправка запросов. Аналогичные бесплатные продукты не подходили по ряду причин: в Fiddler нельзя было быстро сохранять запросы, в Soap UI не хватало его возможностей, ну и как обычно хотелось разобраться. Как раз, подвернулась проблема с утечками памяти, которая утекала медленно, но всё же утекала. Я написал ещё одно приложение: оно позволяло удобно сохранять, конфигурировать запросы, а так же на базе сохранённых запросов делать нагрузочное тестирование с отображением небольшой статистики, что оказалось очень полезным. При реализации новой функциональности можно было устроить локально небольшую DDoS-атаку, для проверки надёжности и выявления неочевидных багов. К тому же было очень удобно сохранять запросы в одном месте, а не каждый раз искать как его сконфигурировать.

Преобразователь текста
Затем возникла идея написать свой преобразований текста . Да, есть много готовых и разных, но зачем держать целый парк таких программ, если можно написать один?
Кроме стандартных преобразований вроде (url encode/decode, base64 и т.д.), была необходимость в форматировании: xml, html, cs, json, css, js, sql, fql. Для шаблонов jquery полезным была бы функция преобразования html в строку и обратно. Также время от времени нужно было сишные строки преобразовывать . В общем очень полезная получилась вещь.

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

Переломный момент
 И вот так постепенно у меня получилось 4 приложения, каждое из которых отнимало ~50Мb по процесс монитору (ох уж этот wpf). Проекты были очень большие, поэтому оперативной памяти хронически не хватало и эти 200Mb можно было явно потратить на что-то более полезное. Вдвойне обидно было то, что на моём родном С ++ и WinApi это всё счастье вместе потребляло бы ну максимум мегабайта 2. Стоп… Зачем мне 4 приложения? Ведь их можно объединить в одно! Сказано сделано. За пару вечеров написана небольшая система с поддержкой плагинов и объединяющая это всё в одно приложение и вуаля - вместе занимают 50 мегабайт. Конечно не идеально, но уже гораздо лучше.
В дальнейшем все новые утилиты я добавлял в виде плагинов, благо интерфейс получился простым и удачным. Также прикрутил общую систему для хранения файлов конфигурации и постарался всё унифицировать по максиму, чтобы сократить все издержки на разработку. Так же была добавлена возможность задания горячих клавиш для любой команды, планировщик (например можно обновлять проекты и запускать переиндексацию для поиска). Кроме обычного меню ещё добавил альтернативный интерфейс, возможность группировать команды, а также поддержку сменных тем. Добавил возможность использования для отдельных команд разработчиков своих специализированных скриптов, плагинов, библиотек и т.д. 

Генератор текста
При повседневной работе столкнулся с тем, что время от времени для тестов надо было генерировать тексты определённой длины, Guid, а также вставлять в Excel всякие таблицы со статистикой на базе простого текста. Написал плагин и для этого. В сочетании с горячими клавишами получается очень удобно. Скажем, создание SQL скриптов с данными, где нужно вставлять много разных Guid облегчается очень сильно, т.к. пользоваться стандартным GUID Generator без поддержки горячих клавиш очень медленно. Или, например, вручную проверить правильно ли работает вся цепочка проверки длины пользовательского текста.

Менеджер проектов
Когда работаешь с проектами, включающими множество под проектов, то очень много времени занимает то, что их все надо отдельно обновлять, компилировать, ходить по папкам и запускать всякие хитрые пакетные файлы. При хронической нехватке оперативной памяти каждый раз запускать и останавливать Visual Studio для простой компиляции это явно не выход. Сначала я писал по-старинке для таких целей батники, но потом понял что как то это неудобно, и создал специальный плагин, в котором можно было выбирать набор проектов, для каждого из которого из меню вызывать команды svn, компилировать/обновлять один или все, запускать пакетные файлы с параметрами, которые сохраняются, что очень удобно - я например, постоянно забываю о том что же там надо указывать. Visual Studio 2010 по умолчанию компилирует проекты в однопоточном режиме, но можно указать опциональный параметр /m для многопоточной компиляции, что заметно ускоряет работу. Также полезной оказалась возможность просмотра всех своих изменений по всему репозиторию - мелочь, зато не надо по каждому проекту смотреть чем экономим время.

Перехватчик утечек
Со временем я вспомнил о ещё одной утилите которую я писал для детектирования утечек памяти. Память текла очень медленно и стандартного монитора счётчиков производительности явно не хватало. Тестирование надо было производить даже не часы, а день. Опять же – для экономии памяти, занимающим всем набором утилит, я тоже добавил ее в унифицированное меню. По функциональности она представляет из себя смесь диспетчера задач и системного монитора , но может собирать статистику за очень долгий промежуток времени.
Добавил для неё свою систему для отображения графиков. В университете мы решали разные интересные задачки для которых было полезным отображение в реальном времени графиков из миллионов точек с масштабированием и всякими другими плюшками, плюс железо уровня первого пентиума с 32мб озу заставляло очень серьёзно относится к ресурсам. В свое время я написал такую систему на WinApi, и сейчас сделал аналогичную на WPF.

Менеджер серверов разработчиков
Следующая проблема опять возникла из-за нехватки памяти. Было очень много сервисов и для старта каждого из них приходилось запускать Visual Studio. С десяток открытых студий с решарпером легко съедят всю доступную память. Добавил простенький плагин который запускал дев сервера и можно было запускать/перезапускать все по одному клику и вуаля, жизнь стала намного легче. Комп перестал тормозить и стало опять можно нормально работать.

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

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

Редактор HTML
Иногда надо набросать часть странички, например диалог, делать это сразу на странице не всегда удобно, иногда это лучше сделать в отдельном окошке. Такая же проблема существует для jquery темплейтов. Сразу их набирать вслепую неудобно, а если они ещё и в одну строчку то тем более. Добавил плагин, на базе IE, в котором 2 окошка - в одном вводишь разметку, а в другом отображается то как она будет выглядеть.

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

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

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

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

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

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

Генератор данных по шаблону
Ещё столкнулся с одной проблемкой. Часто приходится создавать похожие наборы файлов и папок. Например: когда создаём новый проект, когда готовим инфраструктуру для тестов, когда работаем с qunit. Каждый раз одно и тоже? На создание и конфигурирование проекта со всеми плюшками вообще может полдня, а то и день уйти! Ну я как обычно решил облегчить себе жизнь: написал очередной плагин, который эту проблему решает. Создаём шаблон для будущего набора файлов и папок, в имена файлов и папок, а также их содержимое можно добавить имена переменных в спец символах. Потом при запуске плагина можно задавать значения этих переменных. Просто и эффективно. Плюс такой подход практически гарантирует однотипность проектов, что позволит быстрее разбираться с новыми проектами. Так же добавил аналогичную функциональность для работы с текстом, что то вроде снипетов.

Автоматизатор

При разворачивании систем сервисов на рабочих местах новичков столкнулся с той проблемой, что это всё счастье нужно каждый раз настраивать, что часто занимает дни. Ведь проще было бы, чтобы был какой-то аналог инсталлятора, но с простыми командами и возможностью отмены действий. 
Начал писать подобный плагин, пока сделано далеко не всё, но тем не менее уже получилась интересная штука. Все действия описываются на обычном C#, компилируются и запускаются динамически, также можно подставлять широчайший набор параметров и сохранять их для следующего запуска. Вообще проблема параметров очень актуальна для скриптов - параметры очень неудобно задавать вручную, гораздо лучше когда есть какая-то система их хранения.
Хотя я и прилично пописал пакетные файлы и немного на PowerShell, но тем не менее мне как то проще хитрую логику делать на нормальных языках типа с++ или сишарпа, а не писать мало кому понятную магию. Конечно ещё можно использовать вещи типа питона, но к сожалению он не всегда есть на компах (справедливо и для PowerShell), как и права для установки новых программ.
Для нового плагина написал набор скриптов, весьма полезных в работе, например: выбор бд в файлах конфигурации, очистка папок, добавление трэйса в лог, интеграция ресурсов, библиотек и тд. Также этот плагин оказался очень полезным для непрерывной интеграции, даже добавил для него возможность запуска из командной строки, причём настройки он берёт из UI, надо лишь только указать имя профиля, в качестве параметра.
Заметил ещё один плюс - можно написать на неподготовленной машине код на простом сишарпе и задействовать для решения задач. Не надо мучатся с тем как реализовать не простую логику на bat файлах или каждый раз просить админа чтобы он поставил новую программу или дал полный доступ к машине. Как правило любой другой .Net разработчик сможет в cs скрипте быстро разобраться и поправить что надо, а с обычными скриптами не факт что разберётся.

Запуск тестов параллельно
Тесты... тесты это круто, но очень не круто когда их слишком много. Скажем мне пришлось столкнуться с проектом где NUnit тесты выполняются долгие часы. Почему то возможность параллельного их запуска как то мало кого волнует. Надеюсь потому что люди пишут быстрые тесты, а не потому что их не пишут :) Как обычно написал новый плагин, вообще говоря, он запускает тесты на локальной машине, но ничего не мешает добавить туда возможность запуска тестов и по сети, т.к. он работает на основе WCF. Тесты перед запуском делятся следующим образом - первый - 1му агенту, 2й - 2му и тд. потом n+1 - 1му агенту и т.д, где n - количество агентов. Что позволяет без особых проблем выполнять даже не особо стабильные тесты параллельно. Плюс есть возможность запуска тестов в х86 режиме (как ни странно они так работают чуть ли не в 2 раза быстрее), запуск с правами админа, если тесты изменяют локальные данные, то можно настроить клонирование папок с тестами, что с грамотным использованием mklink не так уж и обременительно. Если тесты что то грохают при завершении (например dev servers или chromedriver), можно включить их синхронизацию - т.е. они заканчиваются одновременно. Также можно выполнять приложение перед запуском тестов, для настройки файлов конфигурации или ещё чего нить. В итоге получаем хороший прирост скорости и валидные данные локально, даже если тесты их меняют и почему то упали при выполнении (а по закону подлости они это сделают :) ). Вообще, как по мне, тесты не должны менять локальные данные, но опять же это в идеальном мире.

Итоги
Вот такая история моего небольшого приложения. Я тут описал далеко не всё и не все плагины, а только те что наиболее часто используются. За эти годы утилька выросла до очень полезной программки с почти 1,5Mb исходного кода, даже можно сказать платформы для лёгкой разработки своих велосипедов :). 
Так как моё небольшое приложение заинтересовало большое количество разработчиков, то я добавил автообновление и выложил его в открытый доступ с исходными кодами и документацией, найти его можно тут: TBox . Возможно кому то мой опыт пригодится.
По мере добавления новых возможностей думаю добавлять некоторые заметки о них.

Спасибо за внимание.


6 комментариев:

  1. Очень интересная статья и полезная программа. Продолжайте развивать.

    ОтветитьУдалить
  2. Спасибо за поддержку. Планов по развитию очень много, было бы время :)

    ОтветитьУдалить
  3. I read several first paragraphs of article and conclusion. All mid stuff watched thoughts and some sentences read. So I may miss something. But I guess next can be done:
    1. translate this article into english. there are 100 times more .net devs who does english.
    2. split ui interface and logic so each utility can have cli interface
    3. uploaded separately into nuget or chocolatey. i think am not alone willing to automate work not via ui but with F#(fsx) scripts. put each executable into separate nuget or chocolatey tool package with written use cases and carefully crafted description. add many many samples how to call tools for different tasks. document needed user rights to run and other environment things, like what IIS, what VS, what MSBuild, what .NET tested and targeted.
    3. name things concretely, e.g. if you SQL intercepter works only and tested with NHibernate and may be log4net, name it SQLInterceptor.NHibernate.log4net.
    4. drop tools you may find in web. e.g. if regex and html editors or C# scripts editor. written on .net exists, drop you tools, patch existing tools if license is OK.
    5. learn modern tools. try try F# fsx in Xamarin Studio, try LINQPad, try using scriptcs or cs-script, latest release of MSBuild.

    without this you have tons of dead code published, with what i mentioned you have a change to produce something popular

    ОтветитьУдалить
    Ответы
    1. Hi Dzmitry, thank you for feedback.
      I have published my tools on codeplex: http://tbox.codeplex.com. Also you can find there many other my tools :)
      You can find there english documentation and description.
      Also I have published some of my tools on codeplex.
      This project was started >5 years ago, so you can find there some outdated technologies.
      For some of the tools I have created console versions.
      Unfortunately for now, development is stopped. I have some problems with health and need to resolve them.

      Удалить
  4. I hove you are alive and healthy and on .NET so.

    ОтветитьУдалить
  5. Hi Alex, I set up the tbox tool to run nunit test in parallel. I got the settings page, I added the testsuite name. How do I run them now? I do not see the runner page as described in the documentation

    ОтветитьУдалить