Вчера ради эксперимента решил провести ряд тестов.
У меня есть код на шарпах:
СS codeclass Program
{
static int c;
static void Swap(ref int i, ref int j)
{
c = i;
i = j;
j = c;
}
static void Sort(int[] arr)
{
int temp = 0;
for (int i = 0; i < arr.Length - 1; i++)
{
for (int j = 0; j < arr.Length - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
// меняем элементы местами
Swap(ref arr[j], ref arr[j + 1]);
//temp = arr[j];
//arr[j] = arr[j + 1];
//arr[j + 1] = temp;
}
}
}
}
public static void Main(string[] args)
{
int[] arr = new int[10000];
var rand = new Random();
for (int i = 0; i < arr.Length; i++)
arr[i] = rand.Next();
var sw = new Stopwatch();
sw.Start();
Sort(arr);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Console.ReadKey();
}
}
И аналогичный код на плюсах
CPP code#include <iostream>
#include <time.h>
#include <chrono>
#include <ctime>
using namespace std;
int temp;
void Swap(int& a, int &b)
{
с = a;
a = b;
b = c;
}
int main()
{
srand(time(0));
int *arr; // указатель для выделения памяти под массив
int size = 10000; // размер массива
arr = new int[size];
for (int i = 0; i < size; i++)
arr[i] = rand();
auto start = chrono::system_clock::now();
// Сортировка массива пузырьком
for (int i = 0; i < size - 1; i++)
{
for (int j = 0; j < size - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
//swap(arr[j], arr[j+1]);
Swap(arr[j], arr[j + 1]);
/*temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;*/
}
}
}
auto end = chrono::system_clock::now();
chrono::duration<double> elapsed_seconds = end - start;
time_t end_time = chrono::system_clock::to_time_t(end);
cout << "finished computation at " << elapsed_seconds.count() << "s\n";
system("pause");
}
Проведя замеры разных ситуаций получил следующие результаты:
1) С# с вызовом swap (c объявлена в swap) - ~800мс
2) C# без метода swap (замена в теле for) - ~495мс
3) С# с вызовом swap (c - статическая переменная (без аллокаций на стеке)) - ~590мс
4) С# с вызовом swap (без использования доп. переменной) - ~622мс
5) С++ с вызовом swap (стандартный из std) - ~4000мс
6) С++ с вызовом swap (самописный, аргументы передаются по ссылке, c объявлена в swap) - ~1730мс
7) C++ без метода swap (замена в теле for) - ~470мс
8) С++ с вызовом swap (c - статическая переменная (без аллокаций на стеке)) - ~1690мс
9) С++ с вызовом swap (без использования доп. переменной) - ~1780мс
И самый интересный случай произошел когда я случайно инициализировал переменную объявленную в swap нулем, вместо значения одной из переменных:
10) C++ без инициализации временной переменной 230мс
11) C# без инициализации временной переменной 250мс
Код второго случая в общем виде:
//в c# соответственно ref
void Swap(int& a, int &b)
{
//тут забыл инициализировать
int temp = 0;
a = b;
b = temp;
}
Собственно, вопросы:
1) Почему вызов метода(функции) дает такой большой оверхед по сравнению с вычислениями в теле for?
2) Правильно ли я понимаю, что 10 и 11 случай компилятор хорошо оптимизирует до чего-то подобного
spoilervoid Swap(int& a, int &b)
{
a = b;
b = 0;
}
из-за чего мы избегаем аллокаций на стеке и лишнего побитового копирования?
3) Что нужно было натворить в std::swap что бы она выполнялась в разы дольше маленького самописного Swap?