繁体   English   中英

C ++运行速度比VB6慢

[英]C++ running slower than VB6

这是我在Stack上的第一篇文章。

我通常在VB6中进行开发,但最近开始使用带有g ++编译器库的DEV-C ++ IDE在C ++中进行更多编码。

我对一般程序的执行速度有疑问。

这个旧的VB6代码将在20秒内运行。

DefLng A-Z

Private Sub Form_Load()

    Dim n(10000, 10) As Long
    Dim c(10000, 10) As Long

    For d = 1 To 1000000
        For dd = 1 To 10000
            n(dd, 1) = c(dd, 2) + c(dd, 3)
        Next
    Next

    MsgBox "Done"

End Sub

此C ++代码耗时57秒...

int main(int argc, char *argv[]) {

    long n[10000][10];
    long c[10000][10];

    for (long d=1;d<1000000;d++){
        for (long dd=1;dd<10000;dd++){
            n[dd][1]=c[dd][2]+c[dd][3];   
        }
    }

    system("PAUSE");
    return EXIT_SUCCESS; }

我所做的大多数编码都与AI有关,并且在数组使用上非常繁琐。 我尝试使用int而不是使用long,我尝试了不同的机器,C ++的运行速度至少慢了三倍。

我在傻吗? 谁能解释我在做什么错?

干杯。

简短答案

您需要查看编译器优化设置。 资源可能会有所帮助

总结: C ++允许您使用许多技巧,这些技巧有些通用,有些则取决于您的体系结构,如果使用得当,它将在性能方面优于VB。

长答案

请记住,这高度依赖于您的体系结构和编译器,还取决于编译器设置。 您应该配置编译器以进行更积极的优化。 另外,您应该编写考虑到内存访问的优化代码,明智地使用CPU缓存等。

我已经在使用4.200GHzIntel®Core™i7-7700K CPU内核的ubuntu 16.04虚拟机上为您进行了测试 以下是我使用代码的时间,具体取决于我使用的g ++ 5.4.0编译器的优化级别

我正在使用优化级别0、1、2、3,s,并获得36s(完全未优化),2​​3s,然后.. 零。

osboxes@osboxes:~/test$ g++ a.cpp -O0 -o a0 osboxes@osboxes:~/test$ ./a0 start..finished in 36174855 micro seconds osboxes@osboxes:~/test$ g++ a.cpp -O1 -o a1 osboxes@osboxes:~/test$ ./a1 start..finished in 2352767 micro seconds osboxes@osboxes:~/test$ g++ a.cpp -O2 -o a2 osboxes@osboxes:~/test$ ./a2 start..finished in 0 micro seconds osboxes@osboxes:~/test$ g++ a.cpp -O3 -o a3 osboxes@osboxes:~/test$ ./a3 start..finished in 0 micro seconds osboxes@osboxes:~/test$ g++ a.cpp -Os -o as osboxes@osboxes:~/test$ ./as start..finished in 0 micro seconds

请注意,通过使用更具攻击性的优化级别,编译器将完全消除代码,因为程序中未使用n []中的值。 要强制编译器生成代码,请在声明n时使用volatile关键字

现在添加volatile后,您将获得约12秒钟的最积极的优化(在我的机器上)

osboxes@osboxes:~/test$ g++ a.cpp -O3 -o a3 osboxes@osboxes:~/test$ ./a3 start..finished in 12139348 micro seconds osboxes@osboxes:~/test$ g++ a.cpp -Os -o as osboxes@osboxes:~/test$ ./as start..finished in 12493927 micro seconds

我用于测试的代码(根据您的示例)

#include <iostream>
#include <sys/time.h>
using namespace std;

typedef unsigned long long u64;

u64 timestamp()
{
  struct timeval now;
  gettimeofday(&now, NULL);
  return now.tv_usec + (u64)now.tv_sec*1000000;
}

int main()
{
  cout<<"start"<<endl;
  u64 t0 = timestamp();

  volatile long n[10000][10];
  long c[10000][10];

  for(long d=1;d<1000000;d++)
  {
    for(long dd=1;dd<10000;dd++)
    {
      n[dd][1]=c[dd][2]+c[dd][3];
  }
}

u64 t1 = timestamp();

cout<<"..finished in "<< (t1-t0) << " micro seconds\n";

return 0;
}

多线程

我已经将您的代码转换为使用多线程,使用2个线程,我可以将时间减少一半。

我正在使用这样一个事实,即现在不使用结果,因此内部for不依赖于外部,实际上,您应该找到另一种拆分工作的方式,以使结果不会相互覆盖。

#include <iostream>
#include <sys/time.h>
#include <omp.h>
using namespace std;

typedef unsigned long long u64;

u64 timestamp()
{
  struct timeval now;
  gettimeofday(&now, NULL);
  return now.tv_usec + (u64)now.tv_sec*1000000;
}

int main()
{
omp_set_num_threads(2);
#pragma omp parallel
{
}

cout<<"start"<<endl;
u64 t0 = timestamp();

volatile long n[10000][10];
long c[10000][10];

for(long d=1;d<1000000;d++)
{
#pragma omp parallel for
    for(long dd=1;dd<10000;dd++)
    {
      n[dd][1]=c[dd][2]+c[dd][3];
    }
}

u64 t1 = timestamp();
cout<<"..finished in "<< (t1-t0) << " micro seconds\n";
return 0;
}

osboxes@osboxes:~/test$ g++ a.cpp -O3 -fopenmp -o a3 osboxes@osboxes:~/test$ ./a3 start..finished in 6673741 micro seconds

更新 :与VB6编译器相比,最新的c ++编译器提供了更好的结果。

上面显示的VB6代码不能反映实际情况,在这种情况下,可以更改访问索引,并且可以将计算分解为包含的功能。 更多的经验表明,当使用的数组作为函数输入(通过引用)传递时,VB6具有极大的优化优势。

我尝试了几种c ++编译器,并重写了基准代码,并在其中添加了一些随机行为来欺骗优化。 当然,代码可能会更好,欢迎所有建议。 我使用-O3选项来最大化速度,但未使用多线程模式。

结果:

g ++ 8.1可以在const索引访问(编译器已知,OP中显示的情况)下5秒钟,在动态索引访问下6秒钟内提供最佳的最终结果。 VC 2017仅用于const索引访问需要2秒,而动态情况则需要35秒。

VB6 S1:原始版本,n和c是局部变量。 VB6 S2:内部循环已移入一个函数,n和c是通过引用传递的输入变量。 测试条件:Intel Xeon W3690 / Windows 10

这是测试结果:

执行时间(以秒为单位) 重写代码:

VB6:

DefLng A-Z
Private Declare Function GetTickCount Lib "kernel32" () As Long

Public Sub cal_var(ByRef n() As Long, ByRef c() As Long, id As Long)

        For dd = 0 To 10000
            n(dd, id) = c(dd, id + 1) + c(dd, id + 2)
        Next

End Sub

Public Sub cal_const(ByRef n() As Long, ByRef c() As Long)
        For dd = 0 To 10000
            n(dd, 1) = c(dd, 2) + c(dd, 3)
        Next
End Sub


Private Sub Form_Load()

    Dim n(10001, 10) As Long
    Dim c(10001, 10) As Long

    Dim t0 As Long
    Dim t1 As Long
    Dim t2 As Long

    Dim id As Long
    Dim ret As Long

    t0 = GetTickCount

    For d = 1 To 1000000
        id = d And 7
        Call cal_var(n, c, id) 'For VB S2

        'For dd = 0 To 10000
        '    n(dd, id + 0) = c(dd, id + 1) + c(dd, id + 2)
        'Next

    Next

    t1 = GetTickCount

    For d = 1 To 1000000
        Call cal_const(n, c)  'For VB S2

        'For dd = 0 To 10000
        '    n(dd, 1) = c(dd, 2) + c(dd, 3)
        'Next
    Next

    t2 = GetTickCount

    For d = 0 To 10000
        Sum = Sum + n(d, t0 And 7)
    Next

    MsgBox "Done in " & (t1 - t0) & " and " & (t2 - t1) & " miliseconds"

End Sub

C ++代码:

#include <iostream>
#include <time.h>
#include <string.h>

using namespace std;

#define NUM_ITERATION 1000000
#define ARR_SIZE 10000

typedef long long_arr[ARR_SIZE][10];

void calc_var(long_arr &n, long_arr &c, long id) {
    for (long dd = 0; dd < ARR_SIZE; dd++) {
        n[dd][id] = c[dd][id + 1] + c[dd][id + 2];
    }
}

void calc_const(long_arr &n, long_arr &c) {
    for (long dd = 0; dd < ARR_SIZE; dd++) {
        n[dd][0] = c[dd][1] + c[dd][2];
    }
}

int main()
{
    cout << "start ..." << endl;
    time_t t0 = time(NULL);

    long_arr n;
    long_arr c;

    memset(n, 0, sizeof(n));
    memset(c, t0 & 1, sizeof(c));

    for (long d = 1; d < NUM_ITERATION; d++) {
        calc_var(n, c, (long)t0 & 7);
    }

    time_t t1 = time(NULL);

    for (long d = 1; d < NUM_ITERATION; d++) {
        calc_const(n, c);
    }

    time_t t2 = time(NULL);

    long sum = 0;
    for (int i = 0; i < ARR_SIZE; i++) {
        sum += n[i][t0 & 7];
    }

    cout << "by dynamic index: finished in " << (t1 - t0) << " seconds" << endl;
    cout << "by const index: finished in " << (t2 - t1) << " seconds" << endl;
    cout << "with final result : " << sum << endl;

    return 0;
}

以下原始答案仅基于在VC2008,VB6 S1情况下进行的测试。

原始答案:

我对Progger有相同的结果。 我使用以下代码来避免编译器进行的优化(代码空白):

int main(int argc, char *argv[]) {

    long n[10000][10];
    long c[10000][10];
    long sum = 0;

    memset(n, 0, sizeof(n) );
    memset(c, 0, sizeof(c) );

    for (long d=1;d<1000000;d++){
        for (long dd=1;dd<10000;dd++){
            n[dd][1]=c[dd][2]+c[dd][3];   
        }
    }

    for (long dd=1;dd<10000;dd++){
        sum += n[dd][1];
    }

    return sum; 
}

我使用Visual Studio C ++ 2008编译代码。 事实证明,编译器使用大量带有局部变量的地址重新分配,并与imul(乘法)指令混合使用,这可能会非常昂贵,例如:

.text:00401065                 mov     edx, [ebp+var_C3510]
.text:0040106B                 imul    edx, 28h
.text:0040106E                 mov     eax, [ebp+var_C3510]
.text:00401074                 imul    eax, 28h
.text:00401077                 mov     ecx, [ebp+edx+var_C3500]
.text:0040107E                 add     ecx, [ebp+eax+var_C34FC]
.text:00401085                 mov     edx, [ebp+var_C3510]
.text:0040108B                 imul    edx, 28h
.text:0040108E                 mov     [ebp+edx+var_61A7C], ecx

每次访问索引都需要一个乘法和一个加法指令。

编辑1:问题与C ++中的多维数组访问性能有关。 可以在这里找到更多信息

阵列查询

C ++不提供多维数组,因此科学和工程应用程序可以自己编写或使用可用数组库之一,例如Eigen,Armadillo,uBLAS或Boost.MultiArray。 高质量的C ++数组库通常可以提供出色的性能,但是简单的元素查找速度仍然落后于Fortran。 原因之一是该语言内置了Fortran数组,因此它的编译器可以找出循环中的数组索引步长,并避免为每个元素计算索引的内存偏移量。 我们无法更改C ++编译器,因此下一个最佳解决方案是为多维数组提供线性或平面一维索引运算符,该运算符可用于提高热点循环的性能。

VB版本使用传统方式(一维优化的平面存储器),所使用的指令仅是寄存器添加(eax,edx,esi ...),它们的速度要快得多。

.text:00401AD4                 mov     eax, [ebp-54h]
.text:00401AD7                 mov     ecx, [eax+esi*4+13888h]
.text:00401ADE                 mov     edx, [eax+esi*4+1D4CCh]
.text:00401AE5                 add     ecx, edx
.text:00401AE7                 mov     edx, [ebp-2Ch]
.text:00401AEA                 mov     eax, edi

那可以回答速度问题。 建议:如果可能,您应该使用现有的库(例如:uBlas)。 可以在这里找到更多讨论。

这是C ++版本的机器代码:

.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main           proc near               ; CODE XREF: ___tmainCRTStartup+F6p
.text:00401000
.text:00401000 var_C3514       = dword ptr -0C3514h
.text:00401000 var_C3510       = dword ptr -0C3510h
.text:00401000 var_C350C       = dword ptr -0C350Ch
.text:00401000 var_C3500       = dword ptr -0C3500h
.text:00401000 var_C34FC       = dword ptr -0C34FCh
.text:00401000 var_61A84       = dword ptr -61A84h
.text:00401000 var_61A7C       = dword ptr -61A7Ch
.text:00401000 argc            = dword ptr  8
.text:00401000 argv            = dword ptr  0Ch
.text:00401000 envp            = dword ptr  10h
.text:00401000
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 mov     eax, 0C3514h
.text:00401008                 call    __alloca_probe
.text:0040100D                 mov     [ebp+var_61A84], 0
.text:00401017                 mov     [ebp+var_C350C], 1
.text:00401021                 jmp     short loc_401032
.text:00401023 ; ---------------------------------------------------------------------------
.text:00401023
.text:00401023 loc_401023:                             ; CODE XREF: _main:loc_401097j
.text:00401023                 mov     eax, [ebp+var_C350C]
.text:00401029                 add     eax, 1
.text:0040102C                 mov     [ebp+var_C350C], eax
.text:00401032
.text:00401032 loc_401032:                             ; CODE XREF: _main+21j
.text:00401032                 cmp     [ebp+var_C350C], 0F4240h
.text:0040103C                 jge     short loc_401099
.text:0040103E                 mov     [ebp+var_C3510], 1
.text:00401048                 jmp     short loc_401059
.text:0040104A ; ---------------------------------------------------------------------------
.text:0040104A
.text:0040104A loc_40104A:                             ; CODE XREF: _main+95j
.text:0040104A                 mov     ecx, [ebp+var_C3510]
.text:00401050                 add     ecx, 1
.text:00401053                 mov     [ebp+var_C3510], ecx
.text:00401059
.text:00401059 loc_401059:                             ; CODE XREF: _main+48j
.text:00401059                 cmp     [ebp+var_C3510], 2710h
.text:00401063                 jge     short loc_401097
.text:00401065                 mov     edx, [ebp+var_C3510]
.text:0040106B                 imul    edx, 28h
.text:0040106E                 mov     eax, [ebp+var_C3510]
.text:00401074                 imul    eax, 28h
.text:00401077                 mov     ecx, [ebp+edx+var_C3500]
.text:0040107E                 add     ecx, [ebp+eax+var_C34FC]
.text:00401085                 mov     edx, [ebp+var_C3510]
.text:0040108B                 imul    edx, 28h
.text:0040108E                 mov     [ebp+edx+var_61A7C], ecx
.text:00401095                 jmp     short loc_40104A
.text:00401097 ; ---------------------------------------------------------------------------
.text:00401097
.text:00401097 loc_401097:                             ; CODE XREF: _main+63j
.text:00401097                 jmp     short loc_401023
.text:00401099 ; ---------------------------------------------------------------------------
.text:00401099
.text:00401099 loc_401099:                             ; CODE XREF: _main+3Cj
.text:00401099                 mov     [ebp+var_C3514], 1
.text:004010A3                 jmp     short loc_4010B4
.text:004010A5 ; ---------------------------------------------------------------------------
.text:004010A5
.text:004010A5 loc_4010A5:                             ; CODE XREF: _main+DCj
.text:004010A5                 mov     eax, [ebp+var_C3514]
.text:004010AB                 add     eax, 1
.text:004010AE                 mov     [ebp+var_C3514], eax
.text:004010B4
.text:004010B4 loc_4010B4:                             ; CODE XREF: _main+A3j
.text:004010B4                 cmp     [ebp+var_C3514], 2710h
.text:004010BE                 jge     short loc_4010DE
.text:004010C0                 mov     ecx, [ebp+var_C3514]
.text:004010C6                 imul    ecx, 28h
.text:004010C9                 mov     edx, [ebp+var_61A84]
.text:004010CF                 add     edx, [ebp+ecx+var_61A7C]
.text:004010D6                 mov     [ebp+var_61A84], edx
.text:004010DC                 jmp     short loc_4010A5
.text:004010DE ; ---------------------------------------------------------------------------
.text:004010DE
.text:004010DE loc_4010DE:                             ; CODE XREF: _main+BEj
.text:004010DE                 mov     eax, [ebp+var_61A84]
.text:004010E4                 mov     esp, ebp
.text:004010E6                 pop     ebp
.text:004010E7                 retn
.text:004010E7 _main           endp

VB代码更长,因此我仅在此处发布主要功能:

.text:00401A81 loc_401A81:                             ; CODE XREF: .text:00401B18j
.text:00401A81                 mov     ecx, [ebp-68h]
.text:00401A84                 mov     eax, 0F4240h
.text:00401A89                 cmp     ecx, eax
.text:00401A8B                 jg      loc_401B1D
.text:00401A91                 mov     edx, [ebp-18h]
.text:00401A94                 mov     edi, 1
.text:00401A99                 add     edx, 1
.text:00401A9C                 mov     esi, edi
.text:00401A9E                 jo      loc_401CEC
.text:00401AA4                 mov     [ebp-18h], edx
.text:00401AA7
.text:00401AA7 loc_401AA7:                             ; CODE XREF: .text:00401B03j
.text:00401AA7                 mov     eax, 2710h
.text:00401AAC                 cmp     esi, eax
.text:00401AAE                 jg      short loc_401B05
.text:00401AB0                 mov     ebx, ds:__vbaGenerateBoundsError
.text:00401AB6                 cmp     esi, 2711h
.text:00401ABC                 jb      short loc_401AC0
.text:00401ABE                 call    ebx ; __vbaGenerateBoundsError
.text:00401AC0 ; ---------------------------------------------------------------------------
.text:00401AC0
.text:00401AC0 loc_401AC0:                             ; CODE XREF: .text:00401ABCj
.text:00401AC0                 cmp     esi, 2711h
.text:00401AC6                 jb      short loc_401AD4
.text:00401AC8                 call    ebx ; __vbaGenerateBoundsError
.text:00401ACA ; ---------------------------------------------------------------------------
.text:00401ACA                 cmp     esi, 2711h
.text:00401AD0                 jb      short loc_401AD4
.text:00401AD2                 call    ebx
.text:00401AD4
.text:00401AD4 loc_401AD4:                             ; CODE XREF: .text:00401AC6j
.text:00401AD4                                         ; .text:00401AD0j
.text:00401AD4                 mov     eax, [ebp-54h]
.text:00401AD7                 mov     ecx, [eax+esi*4+13888h]
.text:00401ADE                 mov     edx, [eax+esi*4+1D4CCh]
.text:00401AE5                 add     ecx, edx
.text:00401AE7                 mov     edx, [ebp-2Ch]
.text:00401AEA                 mov     eax, edi
.text:00401AEC                 jo      loc_401CEC
.text:00401AF2                 add     eax, esi
.text:00401AF4                 mov     [edx+esi*4+9C44h], ecx
.text:00401AFB                 jo      loc_401CEC
.text:00401B01                 mov     esi, eax
.text:00401B03                 jmp     short loc_401AA7
.text:00401B05 ; ---------------------------------------------------------------------------
.text:00401B05
.text:00401B05 loc_401B05:                             ; CODE XREF: .text:00401AAEj
.text:00401B05                 mov     ecx, [ebp-68h]
.text:00401B08                 mov     eax, 1
.text:00401B0D                 add     eax, ecx
.text:00401B0F                 jo      loc_401CEC
.text:00401B15                 mov     [ebp-68h], eax
.text:00401B18                 jmp     loc_401A81
.text:00401B1D ; ---------------------------------------------------------------------------
.text:00401B1D
.text:00401B1D loc_401B1D:                             ; CODE XREF: .text:00401A8Bj
.text:00401B1D                 mov     edi, 1
.text:00401B22                 mov     ebx, 2710h
.text:00401B27                 mov     esi, edi
.text:00401B29
.text:00401B29 loc_401B29:                             ; CODE XREF: .text:00401B5Fj
.text:00401B29                 cmp     esi, ebx
.text:00401B2B                 jg      short loc_401B61
.text:00401B2D                 cmp     esi, 2711h
.text:00401B33                 jb      short loc_401B3B
.text:00401B35                 call    ds:__vbaGenerateBoundsError
.text:00401B3B ; ---------------------------------------------------------------------------
.text:00401B3B
.text:00401B3B loc_401B3B:                             ; CODE XREF: .text:00401B33j
.text:00401B3B                 mov     ecx, [ebp-2Ch]
.text:00401B3E                 mov     eax, [ebp-40h]
.text:00401B41                 mov     edx, [ecx+esi*4+9C44h]
.text:00401B48                 add     edx, eax
.text:00401B4A                 mov     eax, edi
.text:00401B4C                 jo      loc_401CEC
.text:00401B52                 add     eax, esi
.text:00401B54                 mov     [ebp-40h], edx
.text:00401B57                 jo      loc_401CEC
.text:00401B5D                 mov     esi, eax
.text:00401B5F                 jmp     short loc_401B29
.text:00401B61 ; ---------------------------------------------------------------------------
.text:00401B61
.text:00401B61 loc_401B61:                             ; CODE XREF: .text:00401B2Bj
.text:00401B61                 mov     ebx, ds:__vbaStrI4
.text:00401B67                 mov     ecx, 80020004h
.text:00401B6C                 mov     [ebp-0B4h], ecx
.text:00401B72                 mov     [ebp-0A4h], ecx
.text:00401B78                 mov     [ebp-94h], ecx
.text:00401B7E                 mov     ecx, [ebp-40h]
.text:00401B81                 mov     eax, 0Ah
.text:00401B86                 push    offset aDone    ; "Done : "

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM