«Go»: количество го-программ

«Го-программы» (goroutines) — это термин языка «Go», это функции, которые выполняются параллельно, что-то вроде местных потоков. Я пока ещё почти не умею с ними работать, только учусь. Одна из особенностей, на которую я сразу наткнулся, то что в языке есть специальный вызов «GOMAXPROCS» (модуль runtime), который указывает сколько процессоров будет задействованно, чтобы запускать го-программы.

По-умолчанию, «Go» использует только один процессор, вот цитата:

It should be smarter and one day it will be smarter, but until it is if you want CPU parallelism you must tell the run-time how many goroutines you want executing code simultaneously. There are two related ways to do this. Either run your job with environment variable GOMAXPROCS set to the number of cores to use (default 1); or import the runtime package and call runtime.GOMAXPROCS(NCPU).
В документации есть множество мест, где говорится, что сейчас планировщик го-программ работает очень плохо, его будут улучшать и однажды «GOMAXPROCS» уйдёт в историю; а пока приходится выставлять это значение вручную.

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

import ("syscall"; "runtime")

func main() {
    if n, _ := syscall.SysctlUint32("hw.ncpu"); n > 0 {
        runtime.GOMAXPROCS(int(n))
    }
}
14 мая 2011 21:57

Orcinus Orca (orcinus.ru)
15 мая 2011, 08:51

Может быть авторы языка решили, что основная аудитория будет использовать один поток и все. А те кому нужна многопоточность то они выставят нужное количество потоков вручную. Кстати, расчет количества процессоров идет на уровне компиляции или исполнения? В документаци на функцию есть где-нибудь указание на этот факт? А то скомпилируешь на 4 процессорном, а выполнишь на 2 и будет прога подглючивать.

bolk (bolknote.ru)
15 мая 2011, 11:34, ответ предназначен Orcinus Orca (orcinus.ru):

Кстати, расчет количества процессоров идет на уровне компиляции или исполнения?
На уровне выполнения, это простой вызов sysctl, такой же как использует одноимённая утилита (http://ru.wikipedia.org/wiki/Sysctl). Этот вызов умеет очень многое рассказывать о системе, в том числе и про процессоры.

Кстати, этот код, конечно, не платформонезависим, под Windows вызова sysctl не существует, насколько я знаю.

zg (zg.livejournal.com)
15 мая 2011, 12:28, ответ предназначен Orcinus Orca (orcinus.ru):

а выполнишь на 2 и будет прога подглючивать.
с чего бы?

bolk (bolknote.ru)
15 мая 2011, 12:41, ответ предназначен Orcinus Orca (orcinus.ru):

А то скомпилируешь на 4 процессорном, а выполнишь на 2 и будет прога подглючивать.
Программа, конечно, не будет подглючивать, просто выполняться будет медленнее, чем могла бы.

Кстати, под Windows можно проверить значение переменной окружения NUMBER_OF_PROCESSORS. Она точно есть со времён XP, возможно она была и раньше.

mixa (mixa.livejournal.com)
15 мая 2011, 15:49, ответ предназначен bolk (bolknote.ru):

Кстати, под Windows можно проверить значение переменной окружения NUMBER_OF_PROCESSORS. Она точно есть со времён XP, возможно она была и раньше.
Это число с учетом гипертрединга, скажем на i5 2 ядра, а NUMBER_OF_PROCESSORS равен 4. Выполнение приложение в 4 потока в таком случае будет не быстрее, чем в два. Впрочем, едва ли заметно хуже.

zg (zg.livejournal.com)
15 мая 2011, 17:03, ответ предназначен mixa (mixa.livejournal.com):

Выполнение приложение в 4 потока в таком случае будет не быстрее, чем в два.
странно, Fritz Chess Benchmark показывает прирост производительности в 1.44 раза. и это у меня ещё относительно древний проц. а вы чем измеряли?

mixa (mixa.livejournal.com)
15 мая 2011, 19:24, ответ предназначен zg (zg.livejournal.com):

а вы чем измеряли?
Да накидал простой тест на яве, который делает много простых арифметических операций. При увеличении количества потоков с одного до двух производительность выросла почти ровно в 2 раза, до 4-х или более -- осталась практически такая же, как с двумя.

На более сложных тестах, возможно, и правда будет прирост, не зря же этот гипертридинг вообще придумали.

anonymouse (инкогнито)
15 мая 2011, 20:06

А что со сборкой мусора? И как происходит диспетчеризация потоков? В erlang/otp задавается число планировщиков процессов(насколько я понял, GOMAXPROCS тоже про это), исполняющих параллельно процессы vm и соответсвенно каждый планировщик может запускать gc для своих процессов, причем у каждого процесса vm своя куча. Интересно, как дела обстоят в Go.

Спасибо.

anonymouse (инкогнито)
15 мая 2011, 20:14

s/задавается/задается/g
s/соответсвенно/соответственно/g

bolk (bolknote.ru)
15 мая 2011, 21:03, ответ предназначен anonymouse

А что со сборкой мусора?
Go сам убирает мусор, но не имеет деструкторов, к примеру, о закрытии файлов надо заботиться самостоятельно, хотя для этого есть удобные средства.
И как происходит диспетчеризация потоков?
Плохо происходит, если честно. Я завершу эксперименты, ещё расскажу об этом.
В erlang/otp задавается число планировщиков процессов(насколько я понял, GOMAXPROCS тоже про это), исполняющих параллельно процессы vm и соответсвенно каждый планировщик может запускать gc для своих процессов…
В Go не процессы и даже не потоки, там го-программы, правда на уровне системы эти го-процессы могут быть и потоками, но не обязательно, но это не процессы, в спецификации есть упоминание, что го-программы выполняются в одном адресном пространстве.

anonymouse (инкогнито)
15 мая 2011, 21:34

Евгений, я, наверное, не смог донести мысль и очень плохо задал вопрос.
Go сам убирает мусор, но не имеет деструкторов, к примеру, о закрытии файлов надо заботиться самостоятельно, хотя для этого есть удобные средства.
Ужасно. Т.е. 'let it crash' метод в go не работает? И падение го-программы ведет к падению всего рантайма/утечкам памяти?
В Go не процессы и даже не потоки, там го-программы, правда на уровне системы эти го-процессы могут быть и потоками, но не обязательно, но это не процессы, в спецификации есть упоминание, что го-программы выполняются в одном адресном пространстве.
Извините, но я не понял. Поправьте, пожалуйста:
1) Раз го-программы выполняются параллельно в одном адресном пространстве => их || исполняют потоки ОС в рамках рантайма Go.
2) Максимальное число этих потоков мы задаем, тогда как-то и как-то в рантайме должен производить планирование го-программ и раскидывание их по потокам рантайма.
3) Раз адресное пространство шареное, то очевидны проблемы блокированием всего рантайма GC на время сборки.

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

bolk (bolknote.ru)
15 мая 2011, 21:55, ответ предназначен anonymouse

Ужасно. Т.е. 'let it crash' метод в go не работает? И падение го-программы ведет к падению всего рантайма/утечкам памяти?
Падение го-программы ведёт к вызову panic, вызов panic можно перехватить. По поводу утечек памяти пока ничего сказать не могу, литературы по этому поводу нет, а я пока не исследовал.
1) Раз го-программы выполняются параллельно в одном адресном пространстве => их || исполняют потоки ОС в рамках рантайма Go.
Не обязательно. http://ru.wikipedia.org/wiki/Сопрограмма
2) Максимальное число этих потоков мы задаем, тогда как-то и как-то в рантайме должен производить планирование го-программ и раскидывание их по потокам рантайма.
У Go есть плохенький планировщик.
3) Раз адресное пространство шареное, то очевидны проблемы блокированием всего рантайма GC на время сборки.
Увы, пока ничего не могу сказать по этому поводу.
Я просто пытаюсь сравнить с erlang, о внутренностях которого имею некоторое представление, и понять, что же такого есть в Go, чего нет в erlang, который изобрели 20 лет назад.
Не знаю, я Erlang не знаю, но в Go куда более привычный синтаксис.

bolk (bolknote.ru)
15 мая 2011, 22:00, ответ предназначен mixa (mixa.livejournal.com):

Да накидал простой тест на яве, который делает много простых арифметических операций. При увеличении количества потоков с одного до двух производительность выросла почти ровно в 2 раза, до 4-х или более -- осталась практически такая же, как с двумя.
Возможно, Ява не умеет распределять операции так, чтобы они могли хорошо выполняться на HT, там есть особенности. Кроме того, HT реализован так, что некоторые программы могут даже замедлиться.

anonymouse (инкогнито)
15 мая 2011, 22:07, ответ предназначен bolk (bolknote.ru):

Спасибо, за ответы!
Не обязательно. http://ru.wikipedia.org/wiki/Сопрограмма
Это понятно и известно, но мы же говорили про "CPU parallelism" и я к сожалению не знаю, как сопрограммы "прикручиваются" к SMP, если подкините ссылок --- буду признателен!

В целом, ясно, что ничего не ясно :) Успехов в дальнейших исследованиях!

bolk (bolknote.ru)
15 мая 2011, 22:36, ответ предназначен anonymouse

как сопрограммы "прикручиваются" к SMP, если подкинете ссылок — буду признателен!
Если на что-то наткнусь, то кину обязательно!

bolk (bolknote.ru)
22 апреля 2012, 07:44

Примечание: сейчас для определения количества процессоров нужно использовать вызов runtime.NumCPU (http://golang.org/pkg/runtime/#NumCPU).

ebat (инкогнито)
22 ноября 2016, 16:53

Привет из 2016

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)