Это сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

«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))
    }
}
19 комментариев
Orcinus Orca (orcinus.ru) 2011

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

Евгений Степанищев (bolknote.ru) 2011

Комментарий для orcinus.ru:

Кстати, расчет количества процессоров идет на уровне компиляции или исполнения?

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

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

zg (zg.livejournal.com) 2011

Комментарий для orcinus.ru:

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

с чего бы?

Евгений Степанищев (bolknote.ru) 2011

Комментарий для orcinus.ru:

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

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

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

mixa (mixa.livejournal.com) 2011

Комментарий для Евгения Степанищева:

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

Это число с учетом гипертрединга, скажем на i5 2 ядра, а NUMBER_OF_PROCESSORS равен 4. Выполнение приложение в 4 потока в таком случае будет не быстрее, чем в два. Впрочем, едва ли заметно хуже.

zg (zg.livejournal.com) 2011

Комментарий для mixa.livejournal.com:

Выполнение приложение в 4 потока в таком случае будет не быстрее, чем в два.

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

mixa (mixa.livejournal.com) 2011

Комментарий для zg.livejournal.com:

а вы чем измеряли?

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

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

anonymouse 2011

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

Спасибо.

anonymouse 2011

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

Евгений Степанищев (bolknote.ru) 2011

Комментарий для anonymouse:

А что со сборкой мусора?

Go сам убирает мусор, но не имеет деструкторов, к примеру, о закрытии файлов надо заботиться самостоятельно, хотя для этого есть удобные средства.

И как происходит диспетчеризация потоков?

Плохо происходит, если честно. Я завершу эксперименты, ещё расскажу об этом.

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

В Go не процессы и даже не потоки, там го-программы, правда на уровне системы эти го-процессы могут быть и потоками, но не обязательно, но это не процессы, в спецификации есть упоминание, что го-программы выполняются в одном адресном пространстве.

anonymouse 2011

Евгений, я, наверное, не смог донести мысль и очень плохо задал вопрос.

Go сам убирает мусор, но не имеет деструкторов, к примеру, о закрытии файлов надо заботиться самостоятельно, хотя для этого есть удобные средства.

Ужасно. Т. е. ’let it crash’ метод в go не работает? И падение го-программы ведет к падению всего рантайма/утечкам памяти?

В Go не процессы и даже не потоки, там го-программы, правда на уровне системы эти го-процессы могут быть и потоками, но не обязательно, но это не процессы, в спецификации есть упоминание, что го-программы выполняются в одном адресном пространстве.

Извините, но я не понял. Поправьте, пожалуйста:
1) Раз го-программы выполняются параллельно в одном адресном пространстве => их || исполняют потоки ОС в рамках рантайма Go.
2) Максимальное число этих потоков мы задаем, тогда как-то и как-то в рантайме должен производить планирование го-программ и раскидывание их по потокам рантайма.
3) Раз адресное пространство шареное, то очевидны проблемы блокированием всего рантайма GC на время сборки.

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

Евгений Степанищев (bolknote.ru) 2011

Комментарий для anonymouse:

Ужасно. Т. е. ’let it crash’ метод в go не работает? И падение го-программы ведет к падению всего рантайма/утечкам памяти?

Падение го-программы ведёт к вызову panic, вызов panic можно перехватить. По поводу утечек памяти пока ничего сказать не могу, литературы по этому поводу нет, а я пока не исследовал.

1) Раз го-программы выполняются параллельно в одном адресном пространстве => их || исполняют потоки ОС в рамках рантайма Go.

Не обязательно. http://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0

2) Максимальное число этих потоков мы задаем, тогда как-то и как-то в рантайме должен производить планирование го-программ и раскидывание их по потокам рантайма.

У Go есть плохенький планировщик.

3) Раз адресное пространство шареное, то очевидны проблемы блокированием всего рантайма GC на время сборки.

Увы, пока ничего не могу сказать по этому поводу.

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

Не знаю, я Erlang не знаю, но в Go куда более привычный синтаксис.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для mixa.livejournal.com:

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

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

anonymouse 2011

Комментарий для Евгения Степанищева:

Спасибо, за ответы!

Не обязательно. http://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0

Это понятно и известно, но мы же говорили про «CPU parallelism» и я к сожалению не знаю, как сопрограммы «прикручиваются» к SMP, если подкините ссылок -​-​-​ буду признателен!

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

Евгений Степанищев (bolknote.ru) 2011

Комментарий для anonymouse:

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

Если на что-то наткнусь, то кину обязательно!

Евгений Степанищев (bolknote.ru) 2012

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

ebat 2016

Привет из 2016

Иван Леший 2020

Привет из 2020-го))

Андрей Шмелев-Шампанов 2023

Привет из 2023-го))

Евгений Степанищев 2023

☺☺☺