Показаны сообщения с ярлыком golang. Показать все сообщения
Показаны сообщения с ярлыком golang. Показать все сообщения

четверг, 9 января 2020 г.

Из интересно-прочитанного

       Довольно интересная статья про монорепу в Яндексе - Arc — система контроля версий для монорепозитория. Доклад Яндекса. Прочитав статью понимаешь что никакая это не система контроля версий - это проложение которое работает поверх SVN и по сути занимается его масштабированием.  Вся монорепа по прежнему лежит в SVN, который обеспечивает единственную операцию - commit в trunk. На Arc лежит масштабирование клиентских операций - их фактически перенесли на сервер, работа с pull реквестами и тд. Пул реквестов в оригинальном SVN нет, но ребятам очень хотелось их. Вот они и навертели их поверх SVN. Но если серьезно - мне нравится их подход к решению проблемы. Он проблемно-ориентированный. Они не сказали - надо все выкинуть к херам и два года пилить свою собственную систему управления исходным кодом, а взяли и решили конкретные проблемы с которыми они сталкивались с минимальным риском для компании. Ведь по сути максимум чем они рискуют - это коммитами в рабочих ветках. А эти данные полюбому сохраняются минимум еще в одном месте - на машине разработчика. Как только ветка вливается в монорепу - за ее сохранность отвечает SVN.
      Прочитал перевод интересной статьи про использование unsafe.Pointer и то какие при этом могут возникать проблемы - https://4gophers.ru/articles/unsafe/#.XhcssCq3p26 

воскресенье, 1 сентября 2019 г.

Из Golang в Assembler

       Я в последнее время все меньше пишу про Golang. Наверное потому что пишу в основном про то что меня интересует, а в мире Golang в последнее время меня не так много интересовало.  Недавно был на Golang meetup - из трех докладов интересным в какой-то степени был один, да и тот был интересен в основном отдельными техническими  деталями. Такого чтобы - ах как клево, давно не было. 
       На выходных отсматривал видео с GopherCon 2019. Из того что посмотрел очень понравился доклад Michael McLoughlin про то как они местами переписывают с Golang на Assembler. Ребята на мой взгляд пропагандируют очень здравый подход к оптимизации.  
            Посмотрев этот доклад я с удивлением обнаружил отсылку к докладу с GopherCon Russia. Вообще очень не часто можно встретить в докладах ведущих конференций ссылку на материалы российских конференций. Но в данном случае можно сказать что доклад Marko Kevac о bitmap индексах этого более чем заслужил.  
Для тех кому лень смотреть просто оставлю ссылку на https://github.com/mmcloughlin/avo 

вторник, 25 июня 2019 г.

Похоронил GoRelic

       Сегодня похоронил один из своих open source проектов - GoRelic. Когда я его начал NewRelic был стандартом де факто для мониторинга, а никакой поддержки для приложений на Golang у них не было. Когда NewRelic анонсировал открытие API для плагинописателей - я быстренько налабал этот проект.  Но как показал опыт писать плагины для закрытых систем (одной из которых без сомнения является NewRelic) это бесперспективное занятие. 
        Их API тогда был убогим и не покрывал и 10% функциональности доступной их собственным плагинам. А когда он стал нормальным - появился официальный Golang агент.  Сегодня я выложил последнюю, 7-ю по счету версию. Больше обновлений не будет.          

среда, 20 декабря 2017 г.

Golang sync.Map

      sync.Map в появившийся в Golang 1.9 оказался довольно занятной штуковиной. Я посмотрел видео с конференции:
 Перечитал еще раз слайды - https://github.com/gophercon/2017-talks/blob/master/lightningtalks/BryanCMills-AnOverviewOfSyncMap/An%20Overview%20of%20sync.Map.pdf но все равно не мог четко понять - в каких случаях стоит использовать sync.Map а в каких обычный map[] + mutex. Может недостаточно понятно написано, может просто я такой одаренный. В общем мне чтобы разобраться нужно залезть в исходники, что я собственно и сделал - https://golang.org/src/sync/map.go?s=822:2269#L16
          Итак под капотом sync.Map находятся две map[] - readonly map и dirty map. Readonly map используется для быстрого чтения/записи уже существующих значений. В readonly map храниться не само значение а указатель на него. Для чтений значения используется  atomic.LoadPointer(), для записи - atomic.CompareAndSwapPointer(). Это так называемый fastpath, то есть собственно для чего все и затевалось. И используем мы этот fastpath если пишем или читаем значения по уже существующим ключам. Все остальное - происходит уже не так быстро. В случае если мы вставляем новое значение - оно попадает в dirty map. Все операции с dity map происходят с заблокированным мютексом. После какого-то количества cache miss - попыток чтения новых значений из readonly map происходит промоушен dirty map до readonly map.  После этого все недавно добавленные ключи можно будет читать писать без блокировки. 
        Итого: читаем/пишем по существующим ключам очень быстро (сильно быстрее map + mutex),  потребляем в 2 раза больше памяти на указатели/хэш таблицы(dirty map также хранит все ключи из readonly map). Но размер значений тут никакой роли не играет, так как мы не храним значения а только указатели на них.  Добавление новых ключей и look up по вновь добавленным ключам происходит сильно медленнее обычной map + mutex потому что появляется дополнительный indirection level(указатели), и сами операции с ними - atomic, а это значит много memory barrier-ов.    

понедельник, 18 декабря 2017 г.

Java HashMap vs Golang map[]

      Волею судьбы сейчас я плотно занимаюсь Java, вместо горячо любимого мною Golang. Ну и попутно сравниваю как решены те ил иные задачи в Golang и Java. Сегодня речь пойдет про хэш таблицы. С точки зрения алгоритмов HashMap и map[] практически идентичны - hash table + разрешение коллизий через цепочки. Но это теоретически, а с практической точки зрения реализация значит ничуть не меньше чем выбранные алгоритмы. Итак начнем: исходники HashMap я взял отсюда, а исходники map[] отсюда

Golang map[]

    1. Общее устройство. Данные хранятся в блоках по 8 элементов. Младшие 3 бита хэша используются для идентификации key-value пары внутри блока. Хэш таблица представляет собой массив из таких восьми-элементных блоков.  Первый блок из восьми элементов всегда располагается непосредственно в хэш таблице.  У этого есть важное следствие - Go map[] чувствует себя очень комфортно при количестве коллизий меньше 8 на элемент хэш таблицы. С учетом того что удлинение цепочки коллизий также происходит блоками по 8 элементов, то go map[] вообще весьма устойчива к росту числа коллизий.  Сам блок представляет из себя структуру из 8 однобайтовых значений для хранения младших битов хэшей, затем 8 ключей и 8 значений. В зависимости от типа ключей и значений они могут хранится непосредственно в этой структуре данных либо там могут хранится только указатели на них.
    2. Хэш функция - Go использует в качестве хэш функции встроенную в большинство процессоров функцию AES шифрование(инструкция AESENC). 
    3. Начальный размер хэш таблицы:   
           3.1 Для пустого map[] хэш таблица вообще не создается, 
           3.2 Если при инициализации map capacity указана меньше 8  - то хэш таблица также не создается.
          3.3 Если capacity указанная при создании map[] больше или равно 8, то размер таблицы рассчитывается как ближайшая степень двойки для которой выполняется условие (1 << B) * loadFactor > count, где B - степень двойки, loadFactor - магическое число равное 6.5, count - map[] capacity указанная при инициализации.  
    4. Расширение хэш таблицы. При расширении хэш таблицы учитывается два фактора:
        a. Количество элементов в map[], при этом логика аналогична описанной выше (как при инициализации map[])
        b. Количество цепочек длинной больше 8 элементов. Как я написал выше  Golang map[] оптимизирован для работы с цепочками длинной менее 8 элементов, поэтому реализация заточена на то чтобы сохранить длинну цепочки от одного до двух элементов. Так как счетчик "переполнений"(цепочек длинной более одного элемента) это uint16, то сама логика различается для map размером хэш таблицы меньше (1 << 16) и больше этого значения.
        если размер таблицы меньше чем (1 << 16) то хэш таблица расширяется в случае если количество цепочек с переполнением становится больше чем (1 << B) (где B - степень двойки, размер хэш таблицы). 
       если размер таблицы больше чем (1 << 16) то логика остается той же, но расчет количества "переполнений" становится приблизительным.
      Если принять во внимание то что в одном звене цепочки может находится до 8 элементов - то расширение хэш таблицы случается всегда по первому условию ( количество значений больше чем 6.5 * (1 << B)).  

Java HashMap 

1. Общее устройство HashMap предельно простое. Это классический hash map как он описан в учебниках по программированию. Хэш таблица представляет собой массив Entry, в каждом Entry ключ, значение и указатель на следующий Entry. Никаких хитростей и попыток оптимизировать что-то. Прям скуку смертная да и только.
2. Хэш функция - как я понял вызывается встроенный в объект метод hash() к которому сверху применяется простейшая XOR функция:
3. С начальным размером таблицы все тоже очень грустно - оно всегда равно 16 элементам. Либо ближайшей степени двойки большей чем требуемая capacity.
4. Расширение хэш таблицы реализовано также крайне просто - есть loadFactor, равный 0.75. Как только количество элементов в HashMap превышает текущее capacity на 75% ее размер увеличивается в двое. Расширение HashMap также происходит предельно просто - выделяется новая хэш таблица, в цикле проходимся по старой таблице и перекладываем ее элементы в новую таблицу. 

Вывод

  
В общем признаться честно - java меня разочаровала. Единственное достоинство реализации HashMap - это простота реализации. Все просто и понятно. Негде там ошибиться. 
По сравнению с этим реализация map[] - это вершина инженерной мысли. Как Porshe 911 по сравнению со старым фордом. В map[] экономится каждый бит. Многих оптимизаций без хорошего понимания ассеблера просто не понять. Количество аллоцируемых объектов,  ссылок в Golang map[] на порядок меньше чем в HashMap, а это значит что и  гораздо меньше работы для сборщика мусора. Также гораздо выше memory locality. В Golang даже floating point арифметику лишний раз стараются не использовать, заменяя сдвигами и другими целочисленными операциями.  В общем мне стало немного понятнее почему софт на Java так тормозит.

вторник, 21 ноября 2017 г.

Golang internals

        За что я люблю Golang - так это за то что ему сравнительно легко заглянуть под капот. Golang это единственный известный мне язык программирования runtime которого написан на нем самом а не на C(на 99%,  там есть немного ассемблера). То есть барьер между написанием программ на языке Go и заглядыванием внутрь - сильно ниже чем у того же PHP или Python. 
    Заглянув в runtime/стандартную библиотеку ты получаешь гораздо более глубокое понимание того как на самом деле работают приложения написанные на Golang. Там очень много красивых решений, которым можно поучится. Вообще когда ты читаешь как реализованы production class решения, ты на многие вещи начинаешь смотреть по другому.
         К примеру сортировка. Существует многие десятки алгоритмов сортировки, какой из них выбрать? Казалось бы - смотри на Big O и выбирай. Но это теория. А на практике оказывается что за счет малой константы многие алгоритмы с O(n2) оказываются быстрее чем алгоритмы гарантирующие O(n log n) даже в худшем случае. В общем кому интересно как работает встроенная в Golang сортировка можно почитать здесь: - https://golang.org/src/sort/sort.go  
          Другой пример - реализация map. Как на самом деле реализован тип map ? Какая хэш функция там используется ? Какой первоначальный размер хэш таблицы ? Как он увеличивается с ростом количества данных в map ? Если вам также интересно как и мне - то ответы на все вопросы можно найти здесь - https://golang.org/src/runtime/hashmap.go 

четверг, 2 ноября 2017 г.

Golang runtime

       Не так давно в ходе выкладки в прод memory/cpu лимитов для компонентов наткнулся на интересную особенность поведения Golang runtime. При нехватки CPU ресурсов у приложения написанного на Golang может резко возрастать потребление памяти. Связано это с тем что при увеличении  нагрузки доля CPU выделяемая на сборку мусора уменьшается и в какой-то момент времени мусор начинает генерироваться быстрее чем идет его сборка. 
         В таком режиме go приложение начинает довольно быстро расходовать память и быстро  пристреливается OOM killer-ом. Потом оно перезапускается, но уже с пустыми кэшами. Так как кэши пустые, приложение начинает расходовать еще больше CPU и вероятность того что оно сново уйдет в крутое пике - крайне велика. 
          Первоначальное предположение было - что Go runtime не видит memory limit-ов установленных cgroup. По после чтения исходников я выяснил что go runtime и не пытается увидеть эти лимиты. Действительно, у приложения запущенного без root привилегий не так много возможностей увидеть с какими ограничениями по памяти оно запущено. И даже если у тебя есть root права - эти ограничения будут зависеть от очень многих факторов, и эти факторы будут различаться на каждой поддерживаемой платформе. В общем go runtime просто аллоцирует новые куски памяти до тех пор пока его не прикончит OOM killer или до тех пор пока  операционная система не вернет ему ошибку при очередном вызове mmap(2). В этом случае go runtime падает с паникой.

понедельник, 5 декабря 2016 г.

Полезные Golang-библиотечки от Uber

        Сегодня осмотрел github аккаунт Uber и нашел просто кучу полезного софта написанного на Golang и для Golang. В общем похоже в Uber прям Golang-бум случился. Кроме https://github.com/uber/go-torch про который я уже они написали:

  • https://github.com/uber/ringpop-go -  фреймворк для fault-tolerant statefull приложений. Если ты не  state-less то твоя жизнь автоматически становится намного сложнее и тебе так или иначе придется решать все те проблемы которые решены в этом фреймворке.
  • https://github.com/uber/tchannel-go  - реализация их внутреннего протокола для взаимодействия между компонентами системы
  • https://github.com/uber/terraform - форк terraform.  Плюс систем типа Ansible/Puppet в том что полно модулей написано под практически весь наиболее распространенный софт. Но в больших компаниях большая часть софта - самописная,  поэтому это преимущество сходит на нет. Гораздо важнее становится - что именно из себя представляет та или иная система управления конфигурацией.
  • https://github.com/uber/jaeger -   распределенная система для трэйсинга приложений. Они еще не до конца выложили ее в opensource, но начало уже положено.
  • https://github.com/uber-go/atomic - библиотека atomic типов с синхронизацией
  • https://github.com/uber-go/fx - Uber фреймворк для микросервисов
  • https://github.com/uber-go/ratelimit - библиотека для реализации ограничений по количеству запросов  
  • github.com/uber-go/sally не самая полезная вещь, но в хозяйстве пригодится
  • https://github.com/thriftrw/thriftrw-go Thrift encoding library for Golang
  • https://github.com/yarpc/yarpc-go - еще один RPC фреймворк
  • https://github.com/uber-go/zap - библиотека для логгирования 

пятница, 2 декабря 2016 г.

Профайлинг в Golang

       Недавно мне рассказали про https://github.com/uber/go-torch. Оказался мега-полезной штукой.  Позволяет из стандартных Golang профайлингов строить flame graph:

Еще одна приятная новость - профайлинг в Golang 1.7 практически не оказывает влияния на производительность. Я включал его на продакшен серверах среди белого дня - и даже не увидел заметного скачка в потреблении CPU.

среда, 19 ноября 2014 г.

понедельник, 19 мая 2014 г.

Gocraft gorelic

        Таки да, я снова родил middleware для своего детища - gorelic. Что называется, продвигаю как могу :-)
      https://github.com/yvasiyarov/gocraft_gorelic

пятница, 16 мая 2014 г.

Martini Gorelic

     По просьбам трудящихся написал middleware для интеграции Martini framework и NewRelic:
https://github.com/yvasiyarov/martini_gorelic
Пожелания, улучшения, pull requests - are welcome :-)

вторник, 13 мая 2014 г.

NewRelic middleware for beego

   Потратил вечер чтобы облегчить жизнь пользователям beego framework.  Теперь подключить NewRelic к любому beego приложению можно за 5 минут:
         https://github.com/yvasiyarov/beego_gorelic
Надеюсь кому-нибуть да пригодится :-)

воскресенье, 11 мая 2014 г.

Go web frameworks

      В последнее время появилось много довольно симпатичных веб фраймворков для Go.  Сегодня решил потыкать несколько из них. Ну и сделать небольшой бенчмарк как водится в таких случаях.  
          Все фраймворки тестировались на одном и том же железе - полудохлом макбуке бородатых годов и компилировались с помощью go1.2.1 darwin/amd64. Дабы избежать холиваров сразу скажу:
  • Да, я знаю что бенчмарки Hello word и реального приложения имеют мало общего и я не собираюсь их сравнивать между собой
  • Ежу понятно что приложение на чистом net/http будет быстрее. Цель данного исследования - узнать ту "цену" которую нам приходится платить за удобство использования того или иного фраймворка.    
Для тестирования мы используем siege (конкретно siege -c 100 -b -r 100 ). 
И так, Hello world на чистом net/http (исходники доступны на тут ):

 
Transactions:         10000 hits
Availability:        100.00 %
Elapsed time:          4.62 secs
Data transferred:         0.10 MB
Response time:          0.04 secs
Transaction rate:      2164.50 trans/sec
Throughput:          0.02 MB/sec
Concurrency:         77.89
Successful transactions:       10000
Failed transactions:            0
Longest transaction:         0.14
Shortest transaction:         0.00

      Hello world используюя Martini фраймворк (исходники):
 Transactions:              10000 hits  
 Availability:             100.00 %  
 Elapsed time:              6.87 secs  
 Data transferred:         0.11 MB  
 Response time:              0.06 secs  
 Transaction rate:        1455.60 trans/sec  
 Throughput:              0.02 MB/sec  
 Concurrency:              94.52  
 Successful transactions:    10000  
 Failed transactions:           0  
 Longest transaction:         0.42  
 Shortest transaction:         0.00  
Эти результаты наглядно показывают что за все нужно платить. Даже даже не самый сложный фраймворк отъедает значительное количество ресурсов.
           Следующий подопытный - gocraft. По предоставляемым возможностям он очень похож на martini, скажем так - это фремворки одного уровня. Вот результаты тестов gocraft:
 Transactions:              10000 hits  
 Availability:             100.00 %  
 Elapsed time:              4.71 secs  
 Data transferred:         0.10 MB  
 Response time:              0.04 secs  
 Transaction rate:        2123.14 trans/sec  
 Throughput:              0.02 MB/sec  
 Concurrency:              78.24  
 Successful transactions:    10000  
 Failed transactions:           0  
 Longest transaction:         0.33  
 Shortest transaction:         0.00  
Исходники можно посмтреть тут. Я специально не стал убирать middleware's чтобы тесты между gocraft и martini были максимально честными.  Как вы видите показатели практически идентичны чистому net/http. И значительно быстрее martini. Хотя влияние Martini четко просматривается.
        Следующим подопытным будет revel. По предоставляемым возможностям это фреймворк совсем другого уровня, и сравнивать его с предыдущими кандидатами будет не совсем корректно.  Это видно даже по "hello word". Если у martini и gocraft это просто 10 строк исходного кода, то для revel - это целое приложение с MVC структурой и кучей всяких файлов.  Одно хорошо - все эти файлы генерируются автоматически и мы с помощью одной команды: revel new myapp  получаем готовое приложение. А теперь результаты:
 Transactions:              9872 hits  
 Availability:              98.72 %  
 Elapsed time:              34.14 secs  
 Data transferred:         19.24 MB  
 Response time:              0.22 secs  
 Transaction rate:        289.16 trans/sec  
 Throughput:              0.56 MB/sec  
 Concurrency:              64.71  
 Successful transactions:    9872  
 Failed transactions:          128  
 Longest transaction:         20.17  
 Shortest transaction:         0.00  
Сказать что результаты меня разочаровали - значит ничего не сказать. В принципе на PHP +Yii мы получаем тоже самое без всяких оптимизаций. Плюс к тому Availability не 100%... и это у Hello world...  В общем revel меня разочаровал. В консоли он ругался что-то вроде:
 2014/05/11 16:37:22 reverseproxy.go:141: http: proxy error: dial tcp 127.0.0.1:62188: too many open files  
 2014/05/11 16:37:22 reverseproxy.go:141: http: proxy error: dial tcp 127.0.0.1:62188: too many open files  
 2014/05/11 16:37:22 reverseproxy.go:141: http: proxy error: dial tcp 127.0.0.1:62188: too many open files  
 2014/05/11 16:37:22 reverseproxy.go:141: http: proxy error: dial tcp 127.0.0.1:62188: too many open files  
 2014/05/11 16:37:22 reverseproxy.go:141: http: proxy error: dial tcp 127.0.0.1:62188: too many open files  
 2014/05/11 16:37:22 server.go:1633: http: Accept error: accept tcp [::]:9000: too many open files; retrying in 5ms  
 2014/05/11 16:37:22 reverseproxy.go:141: http: proxy error: dial tcp 127.0.0.1:62188: too many open files  
зачем создателю revel понадобился reverse proxy - ума не приложу.
        Последний кандидат на сегодня - beego. По своему уровню он очень похож на revel - он также гененирует целое приложение с кучей папочек. Но результаты у него куда лучше:
 Transactions:              10000 hits  
 Availability:             100.00 %  
 Elapsed time:              20.27 secs  
 Data transferred:         15.19 MB  
 Response time:              0.18 secs  
 Transaction rate:        493.34 trans/sec  
 Throughput:              0.75 MB/sec  
 Concurrency:              90.49  
 Successful transactions:    10000  
 Failed transactions:           0  
 Longest transaction:         0.71  
 Shortest transaction:         0.00  
Response time в 5 раз выше чем у gocraft, но это объяснимо. В общем мой выбор это beego и gocraft. В зависимости от характера приложния.

пятница, 10 января 2014 г.

Go Lang

Вчера нашел одно место в стандартной библиотеке Go( а именно в пакете database/sql), которое можно бы прооптимизировать. Зарепортил им Issue 7086, думал сейчас патч пришлю. Хер там был. Через 2 часа его прокомментировал Дмитрий Вьюков, сегодня с утра Bradfitz уже прислал патч. Оперативно работают ребята, что тут скажешь. Молодцы! В общем решил что перейду на девелоперскую версию - хватит на релизах сидеть.

пятница, 13 декабря 2013 г.

На почитать


  1. Go 1.2 зарелизили! Добавили новый инструмент для проверки покрытия тестами - http://blog.golang.org/cover и еще множество улучшений. Более детально смотрите http://blog.golang.org/go12 
  2. http://blog.golang.org/go-at-heroku, оказывается у ZooKeeper есть альтернатива, и написана она на Go. Называется Doozer
  3. http://blog.golang.org/building-stathat-with-go - еще на Go написан StatHat. 
  4. http://blog.golang.org/from-zero-to-go-launching-on-google - для тех кто не верит что Google использует Go
  5. Про то как появился web.gohttp://blog.golang.org/real-go-projects-smarttwitter-and-webgo
  6. http://blog.golang.org/normalization - нормализация UTF-8 текста в Go.
  7. Gopher Academy - большое количество статей (сюрпрайз!) про Go :-) Я буду кидать прямые ссылки на самые интересные по мере прочтения.

понедельник, 23 сентября 2013 г.

NSQ - a realtime distributed messaging platform

     Недавно наткнулся на еще один крупный проект написанный на  Go - называется NSQ. Еще одна очередь написанная на Go. Первой подобной системой была  Iron.io, о которой я писал ранее. Основным разработчиком  NSQ является bit.ly, но кроме него эта система используется еще в нескольких крупных проектах. В общем число компаний, использующих Go в продакшене растет, а следовательно растет и рынок Go программистов - что не может не радовать.  

понедельник, 9 сентября 2013 г.

Про Go

     1.  Статья про Go и его использование в Iron.io http://www.businessinsider.com/googles-go-could-replace-java-2013-8
     2.  Оказывается есть очень полезные пакеты, которые также разрабатываются членами Go team, но которые не включены в основное дерево пакетов. Смотрите ссылки на них внизу этой страници http://golang.org/ref/
     3.  Планируется очередная встреча Go сообщества Москвы, со специальным гостем из Go Team - http://goo.gl/V9i0wL

суббота, 24 августа 2013 г.

На почитать

      Про  Go:

Если в кратце - в Go 1.2 должен появится вытесняющий планировщик задач, улучшена поддержка C++ в cgo, сделаны улучшений в encoder/* пакетах, и много-много других оптимизаций.
     Про Linux:

1. Разработка ядра Linux, by Robert Love (интересная фамилия :-)). Уже есть 3-е издание этой книги, но только в английском варианте.
2. Linux System Programing, того же Robert Love
3. Linux in the Nutshell. Снова Robert Love со товарищами. Плодовитый в общем писака - и вообще много чего другого успел сделать.

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