Большинство критиков Go пишут, что-то вроде «тут нет фишки, которая нравилась мне в языке X». Я не понимаю. Есть тур. Есть документация по языку. Куча статей best practice и how to use. Нет так нет. И не надо пытаться писать на Go так как писал на языке X.
Для того же бенчмарка количество иттераций настраивается ручками. Для разных задач разное число, зачем мне для простенькой задачки гонять проц 10 минут. Для чего нужен и как работает interface{} вообще мало кто из критиков (и не только) понимает.
Про «сверхвысокую» информативность сообщений об ошибках я согласен. Проще в сорцах на github найти, чем в гугл. GCC со своим C в сравнении просто великолепен (в купе с google и SO).
Для низкого уровня Go подходит плохо. Тому цена кроссплатформенность. Сейчас есть подвижки в эту сторону и пакет syscall более не развивается, вместо него лепят отдельно для каждой системы по пакету вот. Должно быть проще в перспективе. Я думаю что Go лучше сравнивать с Java. Если смотреть в веб — то c Ruby (revel+gorm хоть и отдалёно, но напоминают rails c его activerecord).
В любом случае я не думаю, что если использовать НЕstruct{}{} — то приложение выжрет всю память, или будет жёстко тормозить. Вот есть такая фишка как struct{}{} — это основной посыл. И эта struct{}{} применима только для пересылки по каналу, когда по сути не важно, что пересылать. В коде такие пересылки будут явно означать (при просмотре), что речь не о передаче данных, а о каком-то сигнале/событии.
nil — это нулевой указатель, всё верно. И он занимает uintptr места. Указатель на struct{}{} будет занимать столько же места, столько же места будет занимать []byte или &struct{ A, B, C int }. Я не призываю использовать указатель на struct{}{}, ибо это вряд ли когда пригодиться. Но хочу подчеркнуть — что struct{}{} — всегда один и тот же экземпляр. Вот например
var a, b struct{}
a == b // true
&a == &b // true
На play.golang.org с раскрытием значения &struct{}{}. interface{}, кстати — это что-то вроде указателя на структуру вида struct{ typePtr, valPrt }. Вот референс (англ.). Разумеется на несуществующий interface{} будет указывать nil ровно как и на любой другой референсный тип.
Go 1.5 сразу формирует столько тредов ОС, сколько ядер у процессора. У ранних версий со старта выделялся только один. Вот golang.org/doc/go1.5#introduction третий пункт. Одна строчка. Но их количество можно ограничить переменной окружения GOMAXPROCS или функцией runtime.GOMAXPROCS()
Если вы запустите пример выше c GOMAXPROCS=1 то тредов ОС будет три. Не два, не один, не четыре и не восемь, а три. При этом, приложению нужно максимум два, учитывая, что time.Sleep() не блокирует тред — то один.
Да, Вы правы, вместо time.Sleep следовало использовать, например
Но это всё не важно — ибо «юзер-спейс треды» не мешают использовать thread local storage. И сколько бы их не было — если все они в основном треде ОС, то из любого из них можно вызывать функции того же OpenGL. Это касается и горутин, но опять же — проще LockOSThread и не разбираться, что и где выполняется. Ведь библиотек, которые требуют выполнение строго во втором или третьем треде ОС нет.
Вот пример без использования каналов, а на чистом sync.WaitGroup play.golang.org/p/_y6NU9tVjt
Этот подход позволяет обойтись без каналов, в некоторых случаях.
Но более того, есть публикации, которые утверждают, что горутины даже не являются потоками ядра, а являются ещ более легковесными механизмами пространства пользователя.
И это так. Но это не значит, что горутины не используют треды ОС.
If a goroutine is blocking, the runtime will start a new OS thread to handle the other goroutines until the blocking one stops blocking.
Например для
package main
import "time"
func comm() (chan<- struct{}, <-chan struct{}) {
c := make(chan struct{})
return c, c
}
func main() {
in, out := comm()
go func(done chan<- struct{}) {
time.Sleep(30*time.Second)
done <- struct{}{}
}(in)
time.Sleep(30*time.Second)
<-out
}
ps -o nlwp $PID будет 4.
Примечание: для go < 1.5 могут быть отличия. Для GOMAXPROCS=1 число станет 3.
Ну ладно, один тред забирает сборщик мусора. Один, может, ещё для чего или просто про-запас. В итоге получается — горутина = тред. Но, только при условии блокирующих операций в горутинах. Вот и всё.
Про то что POSIX thread не процесс — Ваша правда. Я всегда думал иначе. Странно.
Всё время мысленно провожу аналогию с POSIX threads — а это отдельные процессы (по крайней мере в Linux). Отсюда и такие досадные ошибки. Я хотел подчеркнуть, что LockOSThread не заставляет всю программу выполняться в одном треде, а только текущую горутину. Ща исправлю, на более корректное высказывание. Спасибо, Вы очень внимательны.
А вот и нет, и в первом и во втором случае у Вас создаётся канал предназначенный для конкретного значения. Даже если не пересылать ничего, а использовать закрытие канала — нет нужды делать его типа interface{} или bool. В любом случае — это какие-то значения. Вот например размеры play.golang.org, и nil — это тоже значение, в данном случае. Суть в том, что любое количество struct{} будет занимать 0 памяти, а указатель на переменную содержащую struct{}{} будет всегда один и тот же, для всех таких переменных. Иными словами — это ничего в классическом понимании. С таким же успехом, вместо inteface{} можно пулять любой референсный тип, но опять же зачем?
Всё это позволяет отловить некоторые ошибки на этапе компиляции.
Только это. Каналов может быть тьма, ненароком можно и записать куда не стоит. По ссылке на play.golang.org вообще нет двунаправленного канала — один для записи, другой для чтения, вот и всё. Разумеется для простеньких задач (как эти примеры) — это слишком (а может — хорошая практика, приучать себя к этому). Например, в пакете time, такие функции как After и Tick возвращают строго канал только для чтения. Достаточно трудно намудрить так, что бы потом записать в этот канал. Но если да — то выхлоп компилятора чётко укажет на ошибку.
Тысячи вариантов. Только не нужно говорить, что мозгу всё-равно. Особенно при большом количестве кода под утро. Да я люблю Си. Си великолепен. Но некоторые программисты не заботятся о читаемости кода, и Си даёт им такую возможность.
if (0 > printf())
stuff(), stuff2(), i++, j += i++ + ++j; // что, чёрт возьми???
В заголовке статьи написано, о чём она. Я и не думал, что в ней будет технически полезная информация. В результате чего у Вас сложилось такое впечатление?
В купе с gofmt и таким плагином как, например, GoSublime я теперь не хочу писать на других языках, т.к. они не расставляют отступы за меня и не «красивят» код, не говорят о нелепых опечатках. В качестве примера хочу указать CONTRIBUTING.md одного из проектов:
Для оперативной памяти не столь важно O(n) или O(logn), скорость всё-равно на несколько порядков больше чем для диска. А вот связанный список позволяет сократить «оверхэд» до минимума. Стоит только подумать, сколько «лишней» информации придётся хранить в памяти, для того же красно-чёрного дерева. Для тех кто не в курсе, связанный список хранит значение и указатель на следующий элемент, в то время как красно-чёрное дерево: значение, указатель на левое поддерево, указатель на правое поддерево, цвет (если дерево не опирается на левую сторону (LLRB), то ещё и указатель на предка, кстати для такого дерева вставка медленней, чем для «обычного»). На 64-битных машинах (та же Ubuntu 14.04 x86_64) размер указателя — 64 бит. Цвет для скорости работы, скорее всего, тоже будет выровнен по этой границе (ну это уже на усмотрение разработчика).
Проще говоря — то, что справедливо для io операций, не всегда справедливо для оперативной памяти. То же B+дерево в памяти, это в обще маразм.
А что же делать если мне не составляет проблем поднять два аналогичных сервера за Nginx — для http и для http2. Но как делить по протоколу мне не ясно. А так получается, я выпилю оптимизацию для стариков и они начнут тормозить ещё сильней. А ведь это не только нагрузка на клиента. Если вместо одного спрайта я буду пихать 50 картинок подряд. У меня каналов не хватит хватит конечно, я же чисто протестировать. Но сама такая возможность интересует. Я не совсем уверен, что я смогу пулять статику как-то иначе, не прибегая к двум серверам одновременно, так, чтобы оптимизация для стариков работала и для HTTP/2, кто поддерживает. SSL в обязаловку.
Может мне гневные письма писать в. Steam/Blizzard с просьбой разместить на первой странице их сервисов (магазинов) информацию о том что в связи с политикой компании Yota, мы не рекомендуем использовать их подключения к сети интернет, во избежание проблем с предоставлением сервиса
Для того же бенчмарка количество иттераций настраивается ручками. Для разных задач разное число, зачем мне для простенькой задачки гонять проц 10 минут. Для чего нужен и как работает
interface{}
вообще мало кто из критиков (и не только) понимает.Про «сверхвысокую» информативность сообщений об ошибках я согласен. Проще в сорцах на github найти, чем в гугл. GCC со своим C в сравнении просто великолепен (в купе с google и SO).
Для низкого уровня Go подходит плохо. Тому цена кроссплатформенность. Сейчас есть подвижки в эту сторону и пакет syscall более не развивается, вместо него лепят отдельно для каждой системы по пакету вот. Должно быть проще в перспективе. Я думаю что Go лучше сравнивать с Java. Если смотреть в веб — то c Ruby (revel+gorm хоть и отдалёно, но напоминают rails c его activerecord).
runtime.LockOSThread
в каждой горутине — и это гарантирует: горутина=OS тред.struct{}{}
— то приложение выжрет всю память, или будет жёстко тормозить. Вот есть такая фишка какstruct{}{}
— это основной посыл. И этаstruct{}{}
применима только для пересылки по каналу, когда по сути не важно, что пересылать. В коде такие пересылки будут явно означать (при просмотре), что речь не о передаче данных, а о каком-то сигнале/событии.nil
— это нулевой указатель, всё верно. И он занимаетuintptr
места. Указатель наstruct{}{}
будет занимать столько же места, столько же места будет занимать[]byte
или&struct{ A, B, C int }
. Я не призываю использовать указатель наstruct{}{}
, ибо это вряд ли когда пригодиться. Но хочу подчеркнуть — чтоstruct{}{}
— всегда один и тот же экземпляр. Вот например На play.golang.org с раскрытием значения&struct{}{}
.interface{}
, кстати — это что-то вроде указателя на структуру вида struct{ typePtr, valPrt }. Вот референс (англ.). Разумеется на несуществующийinterface{}
будет указыватьnil
ровно как и на любой другой референсный тип.Если вы запустите пример выше c GOMAXPROCS=1 то тредов ОС будет три. Не два, не один, не четыре и не восемь, а три. При этом, приложению нужно максимум два, учитывая, что time.Sleep() не блокирует тред — то один.
Да, Вы правы, вместо
time.Sleep
следовало использовать, напримерНо это всё не важно — ибо «юзер-спейс треды» не мешают использовать thread local storage. И сколько бы их не было — если все они в основном треде ОС, то из любого из них можно вызывать функции того же OpenGL. Это касается и горутин, но опять же — проще LockOSThread и не разбираться, что и где выполняется. Ведь библиотек, которые требуют выполнение строго во втором или третьем треде ОС нет.
Этот подход позволяет обойтись без каналов, в некоторых случаях.
ps -o nlwp $PID
будет 4.Примечание: для go < 1.5 могут быть отличия. Для GOMAXPROCS=1 число станет 3.
Ну ладно, один тред забирает сборщик мусора. Один, может, ещё для чего или просто про-запас. В итоге получается — горутина = тред. Но, только при условии блокирующих операций в горутинах. Вот и всё.
Про то что POSIX thread не процесс — Ваша правда. Я всегда думал иначе. Странно.
LockOSThread
не заставляет всю программу выполняться в одном треде, а только текущую горутину. Ща исправлю, на более корректное высказывание. Спасибо, Вы очень внимательны.Вообще множество аспектов касающихся каналов не освещены в статье, ибо тогда она получилась бы чересчур большой громоздкой.
interface{}
илиbool
. В любом случае — это какие-то значения. Вот например размеры play.golang.org, иnil
— это тоже значение, в данном случае. Суть в том, что любое количествоstruct{}
будет занимать 0 памяти, а указатель на переменную содержащуюstruct{}{}
будет всегда один и тот же, для всех таких переменных. Иными словами — это ничего в классическом понимании. С таким же успехом, вместоinteface{}
можно пулять любой референсный тип, но опять же зачем?Только это. Каналов может быть тьма, ненароком можно и записать куда не стоит. По ссылке на play.golang.org вообще нет двунаправленного канала — один для записи, другой для чтения, вот и всё. Разумеется для простеньких задач (как эти примеры) — это слишком (а может — хорошая практика, приучать себя к этому). Например, в пакете time, такие функции как After и Tick возвращают строго канал только для чтения. Достаточно трудно намудрить так, что бы потом записать в этот канал. Но если да — то выхлоп компилятора чётко укажет на ошибку.
Тысячи вариантов. Только не нужно говорить, что мозгу всё-равно. Особенно при большом количестве кода под утро. Да я люблю Си. Си великолепен. Но некоторые программисты не заботятся о читаемости кода, и Си даёт им такую возможность.
gofmt
и таким плагином как, например,GoSublime
я теперь не хочу писать на других языках, т.к. они не расставляют отступы за меня и не «красивят» код, не говорят о нелепых опечатках. В качестве примера хочу указать CONTRIBUTING.md одного из проектов: Да это же круто. Единый формат — это круто!Проще говоря — то, что справедливо для io операций, не всегда справедливо для оперативной памяти. То же B+дерево в памяти, это в обще маразм.
не хватитхватит конечно, я же чисто протестировать. Но сама такая возможность интересует. Я не совсем уверен, что я смогу пулять статику как-то иначе, не прибегая к двум серверам одновременно, так, чтобы оптимизация для стариков работала и для HTTP/2, кто поддерживает. SSL в обязаловку.