Большая разница между вашими двумя программами заключается в том, что ваш код Go игнорирует ошибки (и будет паниковать или вызывать ошибку, если вам повезет, если вы очистите пул), в то время как ваш код C ++ распространяет ошибки через исключение. Для сравнения:
if p.size == 0 {
fmt.Printf("Attempting to pop from empty pool!\n")
}
против
if(_size == 0){throw std::out_of_range("");}
Существует как минимум три способа 1 сделать сравнение справедливым:
- Может изменить код C ++, чтобы игнорировать ошибку, как вы делаете в Go,
- Измените обе версии на
panic
/ abort
при ошибке.
- Измените версию Go для обработки ошибок идиоматически, 2 , как вы делаете в C ++.
Итак, давайте сделаем их все и сравним результаты 3 :
- Ошибка игнорирования C ++: стена 1.059329s, пользователь 1.050000s + система 0.000000s = 1.050000s ЦП (99,1%)
- C ++ прерывание при ошибке: стена 1.081585s, пользователь 1.060000s + система 0.000000s = 1.060000s ЦП (98.0%)
- Паникуйте по ошибке: истекшее время: 1.152942427s
- Ошибка игнорирования: истекшее время: 1,196426068s
- Go идиоматическая обработка ошибок: прошедшее время: 1.322005119s
- C ++ исключение: стена 1.373458s, пользователь 1.360000s + система 0.000000s = 1.360000s ЦП (99.0%)
Итак:
- Без обработки ошибок C ++ работает быстрее, чем Go.
- С паникой Go становится быстрее, 4 , но все же не так быстро, как C ++.
- С идиоматической обработкой ошибок C ++ тормозит намного больше, чем Go.
Почему? Это исключение на самом деле никогда не происходит в вашем тестовом прогоне, поэтому фактический код обработки ошибок никогда не выполняется ни на одном языке. Но clang
не может доказать, что этого не происходит. И, поскольку вы никогда не catch
исключение где-либо, это означает, что он должен генерировать обработчики исключений и разматывать стеки для каждого неопределяемого кадра на всем протяжении стека. Таким образом, он выполняет больше работы над каждым вызовом и возвратом функции - не много больше работы, но тогда ваша функция выполняет так мало реальной работы, что ненужная дополнительная работа складывается.
1. Вы также можете изменить версию C ++ для обработки ошибок в стиле C или для использования типа Option и, возможно, других возможностей.
2. Это, конечно, требует гораздо больше изменений: вам нужно импортировать errors
, изменить тип возврата Acquire
на (*Node, error)
, изменить тип возврата processAge
на error
, изменить все ваши return
заявления, и добавить как минимум две if err != nil { … }
проверки. Но это должно быть хорошо в Go, верно?
3. Пока я занимался этим, я заменил ваше прежнее boost::timer
на boost::auto_cpu_timer
, так что теперь мы видим время на настенных часах (как в Go), а также время процессора.
4. Я не буду пытаться объяснить почему, потому что я не понимаю этого. С первого взгляда на сборку, он явно оптимизировал некоторые проверки, но я не понимаю, почему он не мог оптимизировать те же проверки без panic
.