📜 ⬆️ ⬇️

Another performance comparison of C ++ and C #

Inspired by this article.

There are three opinions regarding the performance of C ++ and C #.

Those who know (or think they know) C ++ believe that C ++ is several times or even orders of magnitude faster.
Those who know C ++ and C # know that for ordinary tasks, C ++ performance is not necessary, and where necessary, it is possible to optimize the C # code as well. The upper limit of optimization for C ++ is higher than that of C #, but nobody needs such records.
Those who know only C # have never experienced problems with its speed.
')
People from the first category all the time trying to prove their case. At the same time, there are examples of optimized C ++ code and the most pessimized C # code.

An example of a "typical" comparison


image
Any programmer who knows C # will immediately see two errors:
  1. Calling GC.Collect, which negates any optimizations made in runtime for garbage collection.
  2. The use of for loop, which is guaranteed not to eliminate the checking of boundaries for each access to the array

At the same time, in reality no C # programmer will write code with GC.Collect and a very small part of programmers will make an error in the for loop.
What is the point of comparing guaranteed ineffective C # code even with regular C ++ code? Is that to prove their point.

Fair comparison


To compare languages ​​honestly - it is necessary to compare the type code in both languages. That is such a code that can be found in programs with a probability of more statistical error.

For example, I will use the same bubble sorting array.

C ++ tests


For the case of C ++, I will test three options:
  1. C-style array (pointer)
  2. std :: array
  3. std :: vector

Each test will run 100 times and the result will be averaged.
Measurement code
std::chrono::high_resolution_clock::duration measure(std::function<void()> f, int n = 100)
{
	auto begin = std::chrono::high_resolution_clock::now();
	for (int i = 0; i < n; i++)
	{
		f();
	}
	auto end = std::chrono::high_resolution_clock::now();
	return (end - begin) / n;
}


C-style
void c_style_sort(int *m, int n) 
{
	for (int i = 0; i < N - 1; i++)
		for (int j = i + 1; j < N; j++) {
			if (m[i] < m[j])
			{
				int tmp = m[i];
				m[i] = m[j];
				m[j] = tmp;
			}
		}
}

void c_style_test()
{
	int* m = new int[N];

	for (int i = 0; i < N; i++)
	{
		m[i] = i;
	}
	c_style_sort(m, N);
	delete[] m;
}


, , .

std::array
void cpp_style_sort(std::array<int, N> &m)
{
	auto n = m.size();
	for (int i = 0; i < n-1; i++)
		for (int j = i + 1; j < n; j++) {
			if (m[i] < m[j])
			{
				int tmp = m[i];
				m[i] = m[j];
				m[j] = tmp;
			}
		}
}

void cpp_style_test()
{
	std::array<int, N> m;

	for (int i = 0; i < N; i++)
	{
		m[i] = i; 
	}
	cpp_style_sort(m);
}


, , .

C++ , std::array , , . std::array .

std::vector
void vector_sort(std::vector<int> &m)
{
	auto n = m.size();

	for (int i = 0; i < n - 1; i++)
		for (int j = i + 1; j < n; j++) {
			if (m[i] < m[j])
			{
				int tmp = m[i];
				m[i] = m[j];
				m[j] = tmp;
			}
		}
}

void vector_test()
{
	std::vector<int> m;
	m.reserve(N);

	for (int i = 0; i < N; i++)
	{
		m.push_back(i);
	}
	vector_sort(m);
}


std::array. std::vector — , std::array. vector .

C#


:
  1. unsafe ()
  2. System.Collections.Generic.List

«» , GC.Collect, .

, .
        static long Measure(Action f, int n = 100)
        {
            var sw = System.Diagnostics.Stopwatch.StartNew();
            for (int i = 0; i < n; i++)
            {
                f();
            }
            return sw.ElapsedMilliseconds / n;
        }


static void ArrayTest()
{
    var m = new int[N];
    for (int i = 0; i < m.Length; i++)
    {
        m[i] = i;
    }
    ArraySort(m);

}

static void ArraySort(int[] m)
{
    for (int i = 0; i < m.Length - 1; i++)
        for (int j = i + 1; j < m.Length; j++)
        {
            if (m[i] < m[j])
            {
                int tmp = m[i];
                m[i] = m[j];
                m[j] = tmp;
            }
        }
}


— for m.Length ( ). JIT .

unsafe
static unsafe void UnsafeTest()
{
    var m = new int[N];
    fixed(int* ptr = &m[0])
    {
        for (int i = 0; i < N; i++)
        {
            ptr[i] = i;
        }
        UnsafeSort(ptr, N);
    }
}

static unsafe void UnsafeSort(int* m, int n)
{
    for (int i = 0; i < n - 1; i++)
        for (int j = i + 1; j < n; j++)
        {
            if (m[i] < m[j])
            {
                int tmp = m[i];
                m[i] = m[j];
                m[j] = tmp;
            }
        }
}


, , ( ). fixed array, .

List

        static void ListTest()
        {
            var m = new List<int>(N);
            for (int i = 0; i < N; i++)
            {
                m.Add(i);
            }
            ListSort(m);

        }

        static void ListSort(List<int> m)
        {
            var n = m.Count;
            for (int i = 0; i < n - 1; i++)
                for (int j = i + 1; j < n; j++)
                {
                    if (m[i] < m[j])
                    {
                        int tmp = m[i];
                        m[i] = m[j];
                        m[j] = tmp;
                    }
                }
        }


, List JIT, .


Visual Studio 2015, .NET Framework 4.6. Release, .

:
x86x64
(++) -style55ms55ms
(++) std::array0ms (52ms)65ms
(++) std::vector100ms65ms
(C#)67ms90ms
(C#) unsafe63ms105ms
(C#) List395ms390ms

x86 std::array, 0. , C-style .



github.com/gandjustas/PerfTestCSharpVsCPP

« »


C# — . . C++ «» . C++, . , .

C++. , C++ , C++ . .

Update


:

:
x86x64
(++) -style60ms52ms
(++) std::array51ms60ms
(++) std::vector147ms81ms
(C#)67ms90ms
(C#) unsafe63ms105ms
(C#) List395ms390ms
(C# + .NET Native)62ms59ms
(C# + .NET Native) unsafe63ms52ms
(C# + .NET Native) List274ms282ms

.NET Native , C++.
- std::swap .

: github.com/gandjustas/PerfTestCSharpVsCPP

Source: https://habr.com/ru/post/266373/


All Articles