После отправки i
по каналу i
следует заменить на вновь выделенное big.Int
:
if m.Cmp(z) == 0 {
c <- i
i = new(big.Int).Set(i)
}
Это необходимо, поскольку нет гарантии, что fmt.Println
обработаетцелое число получено в строке fmt.Println(<-c)
.* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * При нерегулярном использовании fmt.Println
происходит переключение режима работы, поэтому, если i
не заменяется вновь выделенным big.Int
, и время выполнения переключается обратно на выполнение цикла for в функции factorize
, тогдацикл for перезапишет i
перед печатью - в этом случае программа не распечатает целое число 1st , отправленное по каналу.
Тот факт, что fmt.Println
может вызвать переключение между циклами, что означает, что цикл for в функции factorize
может потенциально потребляет много времени ЦП между моментом, когда программа main
получает из канала c
, и моментом, когдаmain
процедура завершается.Примерно так:
run factorize()
<-c in main()
call fmt.Println()
continue running factorize() // Unnecessary CPU time consumed
return from fmt.Println()
return from main() and terminate program
Еще одна причина небольшого многоядерного ускорения - выделение памяти .Функция (*Int).Mod
внутренне использует (*Int).QuoRem
и будет создавать новый big.Int
при каждом вызове.Чтобы избежать выделения памяти, используйте QuoRem
напрямую:
func factorize(n *big.Int, start int, step int, c chan *big.Int) {
var q, r big.Int
i := big.NewInt(int64(start))
s := big.NewInt(int64(step))
z := big.NewInt(0)
for {
q.QuoRem(n, i, &r)
if r.Cmp(z) == 0 {
c <- i
i = new(big.Int).Set(i)
}
i.Add(i, s)
}
}
К сожалению, в планировщике goroutine в версии Go r60.3
есть ошибка, которая не позволяет этому коду использовать все ядра ЦП.Когда программа запускается с -n=2
(GOMAXPROCS = 2), время выполнения будет использовать только 1 поток.
Go еженедельный выпуск имеет лучшее время выполнения и может использовать 2потоки, если n=2
передается в программу.Это дает ускорение примерно на 1,9 на моей машине.
Еще один потенциальный фактор, способствующий замедлению многоядерных процессоров, был упомянут в ответе пользователя "High Performance Mark".Если программа разбивает работу на несколько подзадач, и результат получается только из одной подзадачи, это означает, что другие подзадачи могут выполнять некоторую «дополнительную работу».Запуск программы с n>=2
может в общей сложности потребляет больше процессорного времени, чем запуск программы с n=1
.
Узнать, сколько дополнительной работы выполняется, вы можете захотеть (каким-то образом) распечатать значения всех i
во всех процедурах в момент выхода из функции main()
.