Фон
Сегодня, проведя контрольные тесты, мы с коллегами обнаружили некоторые странные вещи, связанные с производительностью кода С# и кода VB.NET.
Мы начали сравнивать С# и Delphi Prism, вычисляя простые числа, и обнаружили, что Prism был примерно на 30% быстрее. Я понял, что CodeGear оптимизировал код при генерации IL (exe
был примерно в два раза больше, чем С# и имел в нем всевозможные разные IL).
Я решил написать тест в VB.NET, полагая, что компиляторы Microsoft в конечном итоге будут писать по существу тот же IL для каждого языка. Тем не менее, результат был более шокирующим: код работал более чем в три раза медленнее на С#, чем VB с той же самой операцией!
Сгенерированный ИЛ был другим, но не очень-то таким, и я недостаточно хорош, чтобы прочитать его, чтобы понять различия.
Бенчмарки
Я включил код для каждого ниже. На моей машине VB находит 348513 простых чисел примерно в 6,36 секунд. С# находит такое же количество простых чисел в 21.76 секунд.
Компьютерные характеристики и примечания
- Intel Core 2 Quad 6600 @2.4Ghz
Каждая машина, на которой я тестировал, имеет заметную разницу в результатах теста между С# и VB.NET.
Оба консольных приложения были скомпилированы в режиме Release, но в противном случае параметры проекта не были изменены из значений по умолчанию, созданных Visual Studio 2008.
код VB.NET
Imports System.Diagnostics
Module Module1
Private temp As List(Of Int32)
Private sw As Stopwatch
Private totalSeconds As Double
Sub Main()
serialCalc()
End Sub
Private Sub serialCalc()
temp = New List(Of Int32)()
sw = Stopwatch.StartNew()
For i As Int32 = 2 To 5000000
testIfPrimeSerial(i)
Next
sw.Stop()
totalSeconds = sw.Elapsed.TotalSeconds
Console.WriteLine(String.Format("{0} seconds elapsed.", totalSeconds))
Console.WriteLine(String.Format("{0} primes found.", temp.Count))
Console.ReadKey()
End Sub
Private Sub testIfPrimeSerial(ByVal suspectPrime As Int32)
For i As Int32 = 2 To Math.Sqrt(suspectPrime)
If (suspectPrime Mod i = 0) Then
Exit Sub
End If
Next
temp.Add(suspectPrime)
End Sub
End Module
Код С#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace FindPrimesCSharp {
class Program {
List<Int32> temp = new List<Int32>();
Stopwatch sw;
double totalSeconds;
static void Main(string[] args) {
new Program().serialCalc();
}
private void serialCalc() {
temp = new List<Int32>();
sw = Stopwatch.StartNew();
for (Int32 i = 2; i <= 5000000; i++) {
testIfPrimeSerial(i);
}
sw.Stop();
totalSeconds = sw.Elapsed.TotalSeconds;
Console.WriteLine(string.Format("{0} seconds elapsed.", totalSeconds));
Console.WriteLine(string.Format("{0} primes found.", temp.Count));
Console.ReadKey();
}
private void testIfPrimeSerial(Int32 suspectPrime) {
for (Int32 i = 2; i <= Math.Sqrt(suspectPrime); i++) {
if (suspectPrime % i == 0)
return;
}
temp.Add(suspectPrime);
}
}
}
Почему выполнение С# Math.Sqrt()
медленнее, чем VB.NET?