在这个例子中,为什么 VisualBasic (VB.NET) 比 C++ 代码快?

Why VisualBasic (VB.NET) faster than C++ code in this example?

我一直在对迭代计算进行基准测试,这是一个使用雅可比方法的静电拉帕拉斯方程求解器。我用 Visual Basic 和 C++(以及其他语言)编写了相同的算法。出于某种原因,Visual Basic .Net 是最快的。我不明白为什么。由于 C++ 被编译为字节码,因此我希望它更快。我觉得我的 C++ 可能没有得到应有的优化。任何有助于理解为什么 C++ 不比 VisualBasic 快的帮助将不胜感激。谢谢。

在以下代码中,Visual basic 的迭代循环持续约 2.8 秒,而 VisualC++ 需要 4.8 秒

Visual Basic 代码

Imports System.Timers

Module Module1
    Const ARRAYDIM = 500
    Const iterationVMaxError = 0.001

    Sub Main()
        Dim PAArrayPotentials(ARRAYDIM, ARRAYDIM) As Single
        Dim PAArrayIsElectrode(ARRAYDIM, ARRAYDIM) As Boolean

        Console.WriteLine("SpeedTestEMLaplaceSolverVB")
        Console.WriteLine("Start generating electrodes and popuate 2D array")

        'Start generating electrodes
        For iy = 0 To ARRAYDIM - 1
            For ix = 0 To ARRAYDIM - 1
                PAArrayPotentials(iy, ix) = 0 'Default
                PAArrayIsElectrode(iy, ix) = False

                If ix = 20 And (iy > 150 And iy < 350) Then
                    PAArrayPotentials(iy, ix) = 1 'Default
                    PAArrayIsElectrode(iy, ix) = True
                End If

                If ix = 480 And (iy > 150 And iy < 350) Then
                    PAArrayPotentials(iy, ix) = -1 'Default
                    PAArrayIsElectrode(iy, ix) = True
                End If

            Next
        Next

        Console.WriteLine("Finished creating electrodes.")

        Console.WriteLine("Press enter key to start")
        Console.ReadLine()
        Console.WriteLine("Starting iterative Laplace.")

        Dim iteration As Integer = 0
        Dim maxerror As Single = 0
        Dim t0 = System.Diagnostics.Stopwatch.StartNew()

        Do
            maxerror = 0
            For iy = 0 To ARRAYDIM - 1
                For ix = 0 To ARRAYDIM - 1
                    If Not PAArrayIsElectrode(iy, ix) Then
                        Dim sum As Single = 0
                        Dim nvalues As Single = 0

                        If iy > 0 Then
                            sum += PAArrayPotentials(iy - 1, ix)
                            nvalues += 1
                        End If
                        If iy < ARRAYDIM - 1 Then
                            sum += PAArrayPotentials(iy + 1, ix)
                            nvalues += 1
                        End If
                        If ix > 0 Then
                            sum += PAArrayPotentials(iy, ix - 1)
                            nvalues += 1
                        End If
                        If ix < ARRAYDIM - 1 Then
                            sum += PAArrayPotentials(iy, ix + 1)
                            nvalues += 1
                        End If

                        If nvalues > 0 Then
                            Dim newval As Single = sum / nvalues
                            Dim vchange As Single = Math.Abs(newval - PAArrayPotentials(iy, ix))
                            maxerror = Math.Max(vchange, maxerror)

                            PAArrayPotentials(iy, ix) = newval
                        End If
                    End If
                Next
            Next

            iteration += 1

        Loop While maxerror > iterationVMaxError

        Dim deltaT As Long = t0.ElapsedMilliseconds

        Console.WriteLine("Completed, in " + iteration.ToString + " iteration cycles; deltat = " + deltaT.ToString + " miliseconds.")

        Console.WriteLine("Press enter key to close")
        Console.ReadLine()
    End Sub

End Module

Visual C++ 代码

// SpeedTestEMLaplaceSolver2.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <array>
#include <cmath>
#include <chrono>

#define ARRAYDIM 500

//Use normal arrays rather than the std::array
float PAArrayPotentials[ARRAYDIM][ARRAYDIM];
bool PAArrayIsElectrode[ARRAYDIM][ARRAYDIM];

int main()
{
    std::cout << "SpeedTestEMLaplaceSolver2.cpp using standard arrays float PAArrayPotentials[PAWIDTH][PAHEIGHT];\n";
    std::cout << "500 x 500 with 2 electrodes at position 20 and 480. Height of 200 each.\n" <<
        "Left electrode at + 1 V and right electrode at - 1V.\n" <<
        "iterationVMaxError = 1e-3.\n";

    const float iterationVMaxError = 1e-3f;

    //Fill array with zeros and electrodes
    std::cout << "Start generating electrodes and PA.\n";
    for (int iy = 0; iy < ARRAYDIM; iy++) {
        for (int ix = 0; ix < ARRAYDIM; ix++) {
            float * pmyPointPot = &PAArrayPotentials[iy][ix]; //get a reference to the point
            bool * pmyPointIsEl = &PAArrayIsElectrode[iy][ix]; //get a reference to the point

            //Default for all points
            *pmyPointPot = 0;
            *pmyPointIsEl = false;

            //Electrode
            if (ix == 20 && (iy > 150 && iy < 350 )) {
                *pmyPointPot = 1;
                *pmyPointIsEl = true;
            }
            if (ix == 480  && (iy > 20 && iy < 350 ) ) {
                *pmyPointPot = -1;
                *pmyPointIsEl = true;
            }
        }
    }

    std::cout << "Completed generating electrodes and PA.\n";

    std::cout << "Type a letter and enter key to start calculation.\n";
    char stemp[80];
    std::cin >> stemp;
    std::cout << "Start iterative laplace\n";

    auto starttime = std::chrono::steady_clock::now();
    long iteration = 0;

    float perror;
    //Convergence routine
    do {
        perror = 0; //resets
        
        for (int iy = 0; iy < ARRAYDIM; iy++) {
            for (int ix = 0; ix < ARRAYDIM; ix++) {

                if (!PAArrayIsElectrode[iy][ix]) {
                    //Makes the new value the average of surrounding values
                    float sum = 0;
                    float nvalues = 0;
                    if (iy > 0) {
                        sum += PAArrayPotentials[iy - 1][ix];
                        nvalues++;
                    }
                    if (iy < ARRAYDIM - 1) {
                        sum += PAArrayPotentials[iy + 1][ix];
                        nvalues++;
                    }
                    if (ix > 0) {
                        sum += PAArrayPotentials[iy][ix - 1];
                        nvalues++;
                    }
                    if (ix < ARRAYDIM - 1) {
                        sum += PAArrayPotentials[iy][ix + 1];
                        nvalues++;
                    }
                    if (nvalues > 0) {
                        float newVal = sum / nvalues;
                        float vchange = fabs(newVal - PAArrayPotentials[iy][ix]);
                        perror = fmax(vchange, perror);

                        PAArrayPotentials[iy][ix] = newVal; //Set the new calculated value
                    }
                }
            }
        }
        iteration++;
        //std::cout << "iteration: " << iteration << " , perror= " << perror << std::endl;
    } while (perror > iterationVMaxError );

    auto endtime = std::chrono::steady_clock::now();

    std::cout << "Completed, in " << iteration << " iteration cycles; deltat = " << std::chrono::duration_cast<std::chrono::milliseconds>(endtime - starttime).count() << " miliseconds" << std::endl;

    return 0;
}

好的。在进一步研究 VB.Net 比 C++ 快的原因后,我假设问题可能是由于 C++ 编译器造成的。

我下载了英特尔C++编译器并重新编译了程序。 我还将数组更改为 1024x1024 大小,并将停止迭代的条件更改为 maxchange=1e-4。全部在 MS Visual Studio 2019 社区下开发。

基准测试结果为:

Visual C++ (MS): 66356 miliseconds
Visual C++ (Intel): 39013 miliseconds
VB.Net : 62847 miliseconds

所以,现在 C++ 编译代码 (intel) 更快。

这是我使用的代码

// SpeedTestEMLaplaceSolver2.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <array>
#include <cmath>
#include <chrono>

#define ARRAYDIM 1024

//Use normal arrays rather than the std::array
float PAArrayPotentials[ARRAYDIM][ARRAYDIM];
bool PAArrayIsElectrode[ARRAYDIM][ARRAYDIM];

int main()
{
    const float iterationVMaxError = 1e-4f;

    std::cout << "SpeedTestEMLaplaceSolver2.cpp using standard arrays float PAArrayPotentials[PAWIDTH][PAHEIGHT];\n";
    std::cout << "Dimensions: " << ARRAYDIM << "x" << ARRAYDIM << " , iterationVMaxError = " << iterationVMaxError << "\n" <<
        "Left electrode at + 1 V and right electrode at - 1V.\n";

    const int  pos_1_4 = (int)floor(ARRAYDIM / 4);
    const int  pos_3_4 = (int)floor(ARRAYDIM * 3 / 4);

    //Fill array with zeros and electrodes
    std::cout << "Start generating electrodes and PA.\n";
    for (int iy = 0; iy < ARRAYDIM; iy++) {
        for (int ix = 0; ix < ARRAYDIM; ix++) {
            float * pmyPointPot = &PAArrayPotentials[iy][ix]; //get a reference to the point
            bool * pmyPointIsEl = &PAArrayIsElectrode[iy][ix]; //get a reference to the point

            //Default for all points
            *pmyPointPot = 0;
            *pmyPointIsEl = false;

            //Electrode
            if (ix == pos_1_4 && (iy > pos_1_4 && iy < pos_3_4)) {
                *pmyPointPot = 1;
                *pmyPointIsEl = true;
            }
            if (ix == pos_3_4 && (iy > pos_1_4 && iy < pos_3_4) ) {
                *pmyPointPot = -1;
                *pmyPointIsEl = true;
            }
        }
    }

    std::cout << "Completed generating electrodes and PA.\n";

    std::cout << "Type a letter and enter key to start calculation.\n";
    char stemp[80];
    std::cin >> stemp;
    std::cout << "Start iterative laplace\n";

    auto starttime = std::chrono::steady_clock::now();
    long iteration = 0;

    float maxerror =0;
    //Convergence routine
    do {
        maxerror = 0; //resets
        
        for (int iy = 0; iy < ARRAYDIM; iy++) {
            for (int ix = 0; ix < ARRAYDIM; ix++) {

                if (!PAArrayIsElectrode[iy][ix]) {
                    //Makes the new value the average of surrounding values
                    float sum = 0;
                    float nvalues = 0;
                    if (iy > 0) {
                        sum += PAArrayPotentials[iy - 1][ix];
                        nvalues++;
                    }
                    if (iy < ARRAYDIM - 1) {
                        sum += PAArrayPotentials[iy + 1][ix];
                        nvalues++;
                    }
                    if (ix > 0) {
                        sum += PAArrayPotentials[iy][ix - 1];
                        nvalues++;
                    }
                    if (ix < ARRAYDIM - 1) {
                        sum += PAArrayPotentials[iy][ix + 1];
                        nvalues++;
                    }
                    if (nvalues > 0) {
                        float newVal = sum / nvalues;
                        float vchange = fabs(newVal - PAArrayPotentials[iy][ix]);
                        maxerror = fmax(vchange, maxerror);

                        PAArrayPotentials[iy][ix] = newVal; //Set the new calculated value
                    }
                }
            }
        }
        iteration++;
        //std::cout << "iteration: " << iteration << " , perror= " << perror << std::endl;
    } while (maxerror > iterationVMaxError );

    auto endtime = std::chrono::steady_clock::now();

    std::cout << "Completed, in " << iteration << " iteration cycles; deltat = " << std::chrono::duration_cast<std::chrono::milliseconds>(endtime - starttime).count() << " miliseconds" << std::endl;

    return 0;
}

还有 VB.Net

Imports System.Timers

Module Module1
    Const ARRAYDIM = 1024
    Const iterationVMaxError = 0.0001 ' 1e-4

    Sub Main()
        Dim PAArrayPotentials(ARRAYDIM - 1, ARRAYDIM - 1) As Single
        Dim PAArrayIsElectrode(ARRAYDIM - 1, ARRAYDIM - 1) As Boolean

        Console.WriteLine("SpeedTestEMLaplaceSolverVB")
        Console.WriteLine("Dimensions: " + ARRAYDIM.ToString + "x" + ARRAYDIM.ToString + " , iterationVMaxError = " + iterationVMaxError.ToString)
        Console.WriteLine("Start generating electrodes and popuate 2D array")

        Dim pos_1_4 As Integer = Int(ARRAYDIM / 4)
        Dim pos_3_4 As Integer = Int(ARRAYDIM * 3 / 4)

        'Start generating electrodes
        For iy = 0 To ARRAYDIM - 1
            For ix = 0 To ARRAYDIM - 1
                PAArrayPotentials(iy, ix) = 0 'Default
                PAArrayIsElectrode(iy, ix) = False


                If ix = pos_1_4 And (iy > pos_1_4 And iy < pos_3_4) Then
                    PAArrayPotentials(iy, ix) = 1 'Default
                    PAArrayIsElectrode(iy, ix) = True
                End If

                If ix = pos_3_4 And (iy > pos_1_4 And iy < pos_3_4) Then
                    PAArrayPotentials(iy, ix) = -1 'Default
                    PAArrayIsElectrode(iy, ix) = True
                End If

            Next
        Next

        Console.WriteLine("Finished creating electrodes.")

        Console.WriteLine("Press enter key to start")
        Console.ReadLine()
        Console.WriteLine("Starting iterative Laplace.")

        Dim iteration As Integer = 0
        Dim maxerror As Single = 0
        Dim t0 = System.Diagnostics.Stopwatch.StartNew()

        Do
            maxerror = 0
            For iy = 0 To ARRAYDIM - 1
                For ix = 0 To ARRAYDIM - 1
                    If Not PAArrayIsElectrode(iy, ix) Then
                        Dim sum As Single = 0
                        Dim nvalues As Single = 0

                        If iy > 0 Then
                            sum += PAArrayPotentials(iy - 1, ix)
                            nvalues += 1
                        End If
                        If iy < ARRAYDIM - 1 Then
                            sum += PAArrayPotentials(iy + 1, ix)
                            nvalues += 1
                        End If
                        If ix > 0 Then
                            sum += PAArrayPotentials(iy, ix - 1)
                            nvalues += 1
                        End If
                        If ix < ARRAYDIM - 1 Then
                            sum += PAArrayPotentials(iy, ix + 1)
                            nvalues += 1
                        End If

                        If nvalues > 0 Then
                            Dim newval As Single = sum / nvalues
                            Dim vchange As Single = Math.Abs(newval - PAArrayPotentials(iy, ix))
                            maxerror = Math.Max(vchange, maxerror)

                            PAArrayPotentials(iy, ix) = newval
                        End If
                    End If
                Next
            Next

            iteration += 1

        Loop While maxerror > iterationVMaxError

        Dim deltaT As Long = t0.ElapsedMilliseconds

        Console.WriteLine("Completed, in " + iteration.ToString + " iteration cycles; deltat = " + deltaT.ToString + " miliseconds.")

        Console.WriteLine("Press enter key to close")
        Console.ReadLine()
    End Sub

End Module