Вопрос или проблема
Итак, я написал функцию, чтобы проверить, равны ли все элементы массива в C++:
#include <vector>
static inline int all_equal(const std::vector<int> &vals) {
if (vals.size() < 1) {
return -1;
}
int sum = 0;
for (const int &v : vals) {
sum += v;
}
if (sum == 0) {
return 3;
}
return vals.size() * vals[0] == sum;
}
void bench(void) {
std::vector<int> a(10'000'000, 100);
all_equal(a);
// std::cout << "равно? " << all_equal(a) << "\n";
}
int main(void) {
for (int i = 0; i < 100; i++) {
bench();
}
return 0;
}
с следующими флагами компиляции
g++ -o cppmain -O3 -ffast-math main.cc
Я замерил время с помощью time и получил следующие результаты
* time ./cppmain
реальное 0m1.829s
пользовательское 0m0.462s
системное 0m1.367s
Мне было интересно, как Go справится с этой же программой, поэтому я написал это
package main
// import "C" // Чтобы остановить LSP от жалоб на cpp файлы.
func all_eqaul(v *[]int) int {
if len(*v) < 1 {
return -1
}
var sum int
for _, v := range *v {
sum += v
}
if sum == 0 {
return 3
}
if sum == len(*v)*(*v)[0] {
return 1
}
return 0
}
func bench() {
s := make([]int, 10_000_000)
for i := range s {
s[i] = 100
}
// fmt.Println("равно: ", all_eqaul(&s))
all_eqaul(&s)
}
func main() {
for i := 0; i < 100; i++ {
bench()
}
}
и скомпилировал с помощью
go build -o gomain main.go
К моему удивлению, когда я замерил время, я получил
* time ./gomain
реальное 0m1.640s
пользовательское 0m1.562s
системное 0m0.109s
Я не знаю, что я сделал неправильно или правильно ли я интерпретирую вывод time, но как так получилось, что Go быстрее C++ на 200 мс?
Ответ или решение
Вопрос о производительности языков программирования, таких как C++ и Go, часто вызывает интерес и дискуссии. В вашем случае вы сравнивали функцию, которая проверяет, все ли элементы массива одинаковые, на C++ и Go, и обнаружили, что программа на Go выполняется быстрее.
Давайте разберемся, почему это могло произойти, и проанализируем ваш код на обоих языках.
Анализ кода на C++
В вашем коде на C++ вы проверяете, все ли элементы массива равны, суммируя значения элементов и сравнивая сумму с произведением первого элемента и длины массива. Вот основные моменты:
-
Компиляция с оптимизациями: Вы компилировали C++ код с флагами
-O3 -ffast-math
, что включает агрессивные оптимизации. Однако, могли возникнуть ситуации, когда оптимизатор не смог выжать максимум из кода, или в некоторых случаях может быть игнорированы оптимизации из-за сложности конструкции кода. -
Пик производительности: Ваша функция
all_equal
вызывает суммирование всех элементов массива, что занимает O(n) времени. Однако это по сути также делает и версия на Go.
Анализ кода на Go
-
Простота кода: Код на Go создаёт массив и заполняет его в течение O(n) времени, а затем также выполняет сумму. Однако Go компилятор может иметь более эффективные алгоритмы для некоторых операций, особенно для Go 1.18 и выше, что может обеспечивать более быструю работу с массивами на больших объемах данных.
-
Оптимизация зборщика мусора: Go управляет памятью по-другому, и эффективность управления памятью может позволить избегать незначительных задержек, которые могут возникать в C++ из-за различных аспектов управления памятью.
Возможные причины разницы в производительности:
-
Разные подходы к памяти: Go может использовать более эффективные методы для работы с массивами, в то время как C++ может исполнять дополнительные инструкции или затраты на управление памятью в вашем коде.
-
Оптимизация выполнения: Возможно, компилятор Go в вашей версии использовал оптимизации, которые были не доступны компилятору C++ в вашем случае.
-
Запуск программы: Время работы программы также зависит от условий на момент выполнения. Ваша система может иметь разные нагрузки в момент запуска обеих программ, что тоже могло влиять на результаты.
Рекомендации
-
Профилирование кода: Используйте инструменты профилирования для глубокого анализа вашего кода и определения узких мест как в C++, так и в Go.
-
Исследование других алгоритмов: Попробуйте альтернативные алгоритмы, которые могут уменьшить количество операций, выполняемых в функции.
-
Тестирование при разных условиях: Запускайте тесты несколько раз, чтобы учесть любые колебания производительности, и делайте сравнения в аналогичных условиях.
-
Использование SIMD: Рассмотрите возможность использования SIMD-инструкций при работе с большими массивами в C++ для повышения производительности.
Этот анализ показывает, что разница в производительности не обязательно указывает на недостатки одного языка или преимуществ другого. Необходимо углублённо изучать результаты, и оптимизация всегда находится на первом месте для достижения лучших результатов.