4 физических / 8 логических https://pastebin.com/xfVW6g3h. Странно, что результат так сильно от процессора зависит. Надо будет на нормальном железе потестить.
Раз уж пошла такая пьянка с распараллеливанием на других языках, то решил немного доработать этот код. Сделал обычный пул на нативных каналах, на моем ноуте получилось примерно на 20-30% эффективнее, чем в варианте с 30 000 горутин.
package main
import (
"flag"
"math"
"runtime"
"sync"
"sync/atomic"
)
var n uint64
var workers int
var wg sync.WaitGroup
func worker(arr []uint64, inners <- chan uint64) {
for k1 := range inners {
for k2 := k1; k2 <= n/k1; k2++ {
var val uint64
if k1 != k2 {
val = k1 + k2
} else {
val = k1
}
atomic.AddUint64(&arr[k1*k2], val)
}
}
wg.Done()
}
func main() {
flag.Uint64Var(&n,"max", 1000000, "max N")
flag.IntVar(&workers, "workers", runtime.NumCPU() / 2, "num of workers")
flag.Parse()
sqrtN := uint64(math.Sqrt(float64(n)))
arr := make([]uint64, n+1)
pool := make(chan uint64, sqrtN)
for k1 := uint64(1); k1 <= sqrtN; k1++ {
pool <- k1
}
close(pool)
for i := 0; i < workers; i++ {
wg.Add(1)
go worker(arr, pool)
}
wg.Wait()
println(arr[n])
}
Попытался распараллелить "в лоб" на Golang. Правда я не уверен, что не накосячил с синхронизацией, но по идеи если массив не меняется, то получать ссылку на значение можно и без лока. Во всяком случае результирующее значение для миллиарда он считает правильное:
package main
import (
"flag"
"math"
"sync"
"sync/atomic"
)
var wg sync.WaitGroup
func calc(n uint64) uint64 {
arr := make([]uint64, n+1)
for k1 := uint64(1); k1 <= uint64(math.Sqrt(float64(n))); k1++ {
wg.Add(1)
go func(t1 uint64) {
for k2 := t1; k2 <= n/t1; k2++ {
var val uint64
if t1 != k2 {
val = t1 + k2
} else {
val = t1
}
atomic.AddUint64(&arr[t1*k2], val)
}
wg.Done()
}(k1)
}
wg.Wait()
return arr[n]
}
var maxN = flag.Uint64("max", 1000000, "max N")
func main() {
flag.Parse()
println(calc(*maxN))
}
В итоге для миллиарда получилось 111.69 sec, т.е. примерно на 40 sec быстрее, чем на C++. 0xd34df00d если будет возможность, попробуйте протестировать у себя. Интересно какой будет результат.
На мой взгляд некорректно сравнивать код оперирующий int64 с кодом оперирующим int32. Если в C++ варианте заменить int64_t на int32_t, то скорость выполнения у меня возрастает почти в два раза. Так что в данном случае надо везде long использовать.
Т.е. на моем ноуте Golang медленнее С++ чуть более чем в 2 раза. Потребление памяти одинаковое.
Кстати, еще примечательно, что C++ код выполнялся в абсолютной тишине, а от Golang кулеры завизжали как сумасшедшие. Хотя возможно, это как-то связанно со спецификой работы Benchmark в Golang.
PS Слишком долго форматировал сообщение, опередили :D
Если вы покупаете Home Edition, то вы можете выбрать еще 5 аккаунтов, у которых будет полностью такой же набор услуг. У каждого будет свой собственный 1ТБ хранилища, на своим собственном аккаунте.
Возможно он так и делает. В данном случае я больше хотел показать, что в Golang уже все необходимое для измерения производительности есть: golang.org/pkg/testing Так что нету необходимости придумывать велосипеды.
А зачем такой сложный тест? Мне кажется было бы нагляднее, если упростить его до максимума. Тем более, что Golang предоставляет такой замечательный инструмент для тестирования из коробки.
package main
import (
"testing"
"time"
)
var val1 = 10
var val2 = 1000
func longComparison(val int) bool {
time.Sleep(100 * time.Millisecond)
return true
}
func BenchmarkComplexIfDiv(b *testing.B) {
for i := 0; i < b.N; i++ {
if val1 > val2 && val1%2 == 0 {
}
}
}
func BenchmarkComplexIfConj(b *testing.B) {
for i := 0; i < b.N; i++ {
if val1 > val2 && val1&1 == 0 {
}
}
}
func BenchmarkComplexLong(b *testing.B) {
for i := 0; i < b.N; i++ {
if val1 > val2 && longComparison(val1) {
}
}
}
func BenchmarkTwoSimpleIf(b *testing.B) {
for i := 0; i < b.N; i++ {
if val1 <= val2 {
continue
}
if val1%2 == 0 {
}
}
}
// $ go test -bench .
// BenchmarkComplexIfDiv-8 2000000000 0.85 ns/op
// BenchmarkComplexIfConj-8 2000000000 0.85 ns/op
// BenchmarkComplexLong-8 2000000000 0.57 ns/op
// BenchmarkTwoSimpleIf-8 2000000000 0.58 ns/op
Кстати, забавно, но BenchmarkComplexLong такой же быстрый как и вариант с continue
А еще можно скооперироваться и взять Office 356 Home на шестерых. Тогда у каждого будет по полному пакету офиса и 1ТБ хранилища за $1.4 (90 руб) в месяц. Собственно, именно это я и сделал.
P.S. Ах да, еще это включает 60 минут Skype в месяц. Звонить можно во многие страны, в частности в Россию можно тоже, но только на стационарные телефоны. Однако я этим совершенно не пользуюсь.
4 физических / 8 логических https://pastebin.com/xfVW6g3h. Странно, что результат так сильно от процессора зависит. Надо будет на нормальном железе потестить.
Попробую без атомиков реализовать позже.
Раз уж пошла такая пьянка с распараллеливанием на других языках, то решил немного доработать этот код. Сделал обычный пул на нативных каналах, на моем ноуте получилось примерно на 20-30% эффективнее, чем в варианте с 30 000 горутин.
Попытался распараллелить "в лоб" на Golang. Правда я не уверен, что не накосячил с синхронизацией, но по идеи если массив не меняется, то получать ссылку на значение можно и без лока. Во всяком случае результирующее значение для миллиарда он считает правильное:
В итоге для миллиарда получилось 111.69 sec, т.е. примерно на 40 sec быстрее, чем на C++. 0xd34df00d если будет возможность, попробуйте протестировать у себя. Интересно какой будет результат.
Заменил int64 на uint64 и результат получился значительно лучше:
Я думаю Golang достаточно умный чтобы выполнить это один раз. Попробовал вынести это в переменную, но не заметил разницы.
Я компилировал ваш код с теми же аргументами:
clang++ -O3 -march=native -o habr-test-cpp ./habr-test-1.cpp
. Правда, судя по всему, у меня clang 6Вариант на Golang:
И результат на моем древнем ноутбуке с Intel® Core(TM) i7-2860QM CPU @ 2.50GHz и 16 Gb оперативки:
При этом С++ вариант у меня выполняется:
Т.е. на моем ноуте Golang медленнее С++ чуть более чем в 2 раза. Потребление памяти одинаковое.
Кстати, еще примечательно, что C++ код выполнялся в абсолютной тишине, а от Golang кулеры завизжали как сумасшедшие. Хотя возможно, это как-то связанно со спецификой работы Benchmark в Golang.
PS Слишком долго форматировал сообщение, опередили :D
А зачем такой сложный тест? Мне кажется было бы нагляднее, если упростить его до максимума. Тем более, что Golang предоставляет такой замечательный инструмент для тестирования из коробки.
Кстати, забавно, но BenchmarkComplexLong такой же быстрый как и вариант с continue
P.S. Ах да, еще это включает 60 минут Skype в месяц. Звонить можно во многие страны, в частности в Россию можно тоже, но только на стационарные телефоны. Однако я этим совершенно не пользуюсь.
git stash
— отложить все текущие изменения в локальное хранилищеgit stash pop
— забрать последние сохраненные изменения из хранилищаПолезно если вы не хотите пока комитить код, но вам срочно нужно переключится на другую ветку.
Если вы про то, что ключевое слово
const
для определения read-only переменных было выбрано неудачно и вносит путаницу, то тут я полностью согласен.