programing

C#은 왜 수학을 실행합니까?Sqrt()가 VB보다 느립니다.NET?

jooyons 2023. 5. 15. 21:39
반응형

C#은 왜 수학을 실행합니까?Sqrt()가 VB보다 느립니다.NET?

배경

오늘 아침 벤치마크 테스트를 실행하는 동안 동료들과 저는 C# 코드 대 VB의 성능과 관련하여 몇 가지 이상한 점을 발견했습니다.NET 코드.

C#과 C#을 비교하기 시작했습니다.델파이 프리즘은 소수를 계산했고, 프리즘이 약 30% 더 빠르다는 것을 발견했습니다.IL을 생성할 때 CodeGear에 최적화된 코드를 더 많이 파악했습니다.exeC#의 약 두 배 크기에 다양한 IL이 들어 있었습니다.)

저는 VB로 시험을 쓰기로 결심했습니다.또한 NET은 마이크로소프트 컴파일러가 각 언어에 대해 기본적으로 동일한 IL을 작성하게 된다고 가정합니다.하지만, 그 결과는 더 충격적이었습니다: 코드는 같은 작업을 하는 VB보다 C#에서 3배 이상 느리게 실행되었습니다!

생성된 IL은 달랐지만 그다지 그렇지는 않았습니다. 그리고 저는 그 차이를 이해하기 위해 그것을 읽는 것에 능숙하지 않습니다.

벤치마크

아래에 각각의 코드를 포함시켰습니다.제 컴퓨터에서 VB는 약 6.36초 안에 348513 소수를 찾습니다.C#은 21.76초 안에 같은 수의 소수를 찾습니다.

컴퓨터 사양 및 참고 사항

  • Intel Core 2 Quad 6600 @ 2.4Ghz

제가 테스트한 모든 기계는 C#과 VB의 벤치마크 결과에 현저한 차이가 있습니다.그물.

두 콘솔 응용 프로그램 모두 릴리스 모드에서 컴파일되었지만 그렇지 않으면 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

C# 코드

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);
        }

    }
}

C#이 실행되는 이유는 무엇입니까?Math.Sqrt()VB보다 느립니다.NET?

C# 구현이 재계산 중입니다.Math.Sqrt(suspectPrime)루프를 통과할 때마다 VB는 루프 시작 시에만 계산합니다.이는 제어 구조의 특성 때문입니다.C#에서,for그저 공상일 뿐입니다.while루프, VB에서는 별도의 구성입니다.

이 루프를 사용하면 점수가 균일해집니다.

        Int32 sqrt = (int)Math.Sqrt(suspectPrime)
        for (Int32 i = 2; i <= sqrt; i++) { 
            if (suspectPrime % i == 0) 
                return; 
        }

저는 C# 코드가 모든 반복에서 sqrt를 계산한다는 진술에 동의하며, 여기 Reflector에서 직접 나온 증거가 있습니다.

VB 버전:

private static void testIfPrimeSerial(int suspectPrime)
{
    int VB$t_i4$L0 = (int) Math.Round(Math.Sqrt((double) suspectPrime));
    for (int i = 2; i <= VB$t_i4$L0; i++)
    {
        if ((suspectPrime % i) == 0)
        {
            return;
        }
    }
    temp.Add(suspectPrime);
}

C# 버전:

 private void testIfPrimeSerial(int suspectPrime)
{
    for (int i = 2; i <= Math.Sqrt((double) suspectPrime); i++)
    {
        if ((suspectPrime % i) == 0)
        {
            return;
        }
    }
    this.temp.Add(suspectPrime);
}

이는 개발자가 루프 정의에서 sqrt에 대한 호출을 가질 만큼 순진하더라도 VB가 더 나은 성능을 발휘하는 코드를 생성한다는 것을 의미합니다.

다음은 루프에 대한 에서 디컴파일된 IL입니다.이 둘을 비교하면 VB가 나옵니다.네트는 오직Math.Sqrt(...)C#이 각 패스에서 이를 확인하는 동안 한 번.는 이문를해면다음같과은작합수다니야행해업을결과 같은 것을 해야 할 입니다.var sqrt = (int)Math.Sqrt(suspectPrime);다른 사람이 암시한 바와 같이

VB...

.method private static void CheckPrime(int32 suspectPrime) cil managed
{
    // Code size       34 (0x22)
    .maxstack  2
    .locals init ([0] int32 i,
         [1] int32 VB$t_i4$L0)
    IL_0000:  ldc.i4.2
    IL_0001:  ldarg.0
    IL_0002:  conv.r8
    IL_0003:  call       float64 [mscorlib]System.Math::Sqrt(float64)
    IL_0008:  call       float64 [mscorlib]System.Math::Round(float64)
    IL_000d:  conv.ovf.i4
    IL_000e:  stloc.1
    IL_000f:  stloc.0
    IL_0010:  br.s       IL_001d

    IL_0012:  ldarg.0
    IL_0013:  ldloc.0
    IL_0014:  rem
    IL_0015:  ldc.i4.0
    IL_0016:  bne.un.s   IL_0019

    IL_0018:  ret

    IL_0019:  ldloc.0
    IL_001a:  ldc.i4.1
    IL_001b:  add.ovf
    IL_001c:  stloc.0
    IL_001d:  ldloc.0
    IL_001e:  ldloc.1
    IL_001f:  ble.s      IL_0012

    IL_0021:  ret
} // end of method Module1::testIfPrimeSerial

C#...

.method private hidebysig static void CheckPrime(int32 suspectPrime) cil managed
{
    // Code size       26 (0x1a)
    .maxstack  2
    .locals init ([0] int32 i)
    IL_0000:  ldc.i4.2
    IL_0001:  stloc.0
    IL_0002:  br.s       IL_000e

    IL_0004:  ldarg.0
    IL_0005:  ldloc.0
    IL_0006:  rem
    IL_0007:  brtrue.s   IL_000a

    IL_0009:  ret

    IL_000a:  ldloc.0
    IL_000b:  ldc.i4.1
    IL_000c:  add
    IL_000d:  stloc.0
    IL_000e:  ldloc.0
    IL_000f:  conv.r8
    IL_0010:  ldarg.0
    IL_0011:  conv.r8
    IL_0012:  call       float64 [mscorlib]System.Math::Sqrt(float64)
    IL_0017:  ble.s      IL_0004

    IL_0019:  ret
} // end of method Program::testIfPrimeSerial

VS2010을 사용하여 실행 중인 경우 PLINQ를 활용하여 C#(VB로 추정)을 만들 수 있습니다.네트워크 속도도 향상됩니다.

루프를...로 변경합니다.

var range = Enumerable.Range(2, 5000000);

range.AsParallel()
    .ForAll(i => testIfPrimeSerial(i));

저는 제 기계에서 7.4초에서 4.6초로 뛰었습니다.릴리스 모드로 이동하면 그 위에 시간이 조금 더 단축됩니다.

차이는 루프에 있습니다. C# 코드는 반복할 때마다 제곱근을 계산합니다.다음에서 한 줄 변경:

for (Int32 i = 2; i <= Math.Sqrt(suspectPrime); i++) {

대상:

var lim = Math.Sqrt(suspectPrime);
for (Int32 i = 2; i <= lim; i++) {

제 기계의 시간을 26초에서 7초로 낮췄습니다.

일반적으로 아닙니다.둘 다 CLR(공통 언어 런타임) 바이트 코드로 컴파일됩니다.이는 JVM(Java Virtual Machine)과 비슷합니다.

언급URL : https://stackoverflow.com/questions/3025968/why-does-c-sharp-execute-math-sqrt-more-slowly-than-vb-net

반응형