将 C++ 类优化为与 C-struct 一样快

Posted

技术标签:

【中文标题】将 C++ 类优化为与 C-struct 一样快【英文标题】:Optimizing a C++ class to be as fast as a C-struct 【发布时间】:2015-09-22 12:48:07 【问题描述】:

下面的类定义可以改变什么来创建和销毁这些对象,并尽可能快地将它们作为函数参数传递?改用类的原因是需要运算符重载。

类定义:

class MyType 
    int _a, _b, _c;
    MyType(int a, int b, int c) : _a(a), _b(b), _c(c) 
    MyType(MyType & mt) : a(mt.a), b(mt.b), c(mt.c) 
    ~MyType() 
    MyType operator+(MyType & op) /* do something */

结构体定义:

struct MyTypeC 
    int a; int b; int c;
;

而且无论是否使用这两种类型,都应该以几乎相同的 CPU 时间运行的程序:

void f(MyType & mt) mt; return;
void g(MyTypeC & mtc) mtc; return;

void TestCpp() 
    for (int i=1e5; --i; ) 
        MyType mt(0, 1, 2);
        f(mt);
    


void TestC() 
    for (int i=1e5; --i; ) 
        MyTypeC mtc = .a=0, .b=1, .c=2;
        g(mtc);
    

即,可以在 MyType 中进行哪些更改以使运行函数 TestCpp() 的 CPU 时间几乎与运行函数 TestC() 一样快?

编辑:

为 MyType 添加了完整的成员函数列表。

这些是每个相应函数的一些运行时 CPU 计时:

time (TestC)   = 0.000618 s
time (TestCpp) = 0.001373 s

【问题讨论】:

如果它已经不那么快了,我会感到惊讶......时序分析在优化构建上显示了什么? 您是否尝试过使用分析器?性能有何不同? 为什么你认为它会比struct 慢?在 C++ 中,structclass 的同义词,但默认情况下具有公共访问权限。 "使用类的原因是需要运算符重载。" - 你可以重载结构体的操作符,就像你可以重载类一样。 旁注:我想知道有多少“铁杆 C”程序员在他们的脑海中有着同样的神话。 【参考方案1】:

你的问题的答案是双重的:

    您的源代码甚至没有编译,因此我假设您从未运行过代码。因此,您的问题不是基于任何观察。两者都同样快。此外,如果您运行了代码的优化版本,您会看到优化器生成的机器代码几乎等同于两种情况下的 NOP。 IE。它们的速度相同。 structclass 在 C++ 中几乎相同。类的默认访问是私有的(这是您的代码无法编译的原因之一 - 私有构造函数);对于结构来说是公开的。您可以使用结构派生和模板以及诸如此类的东西,就像使用类一样。只要类代码和结构代码相同,就没有区别。

只是为了好玩:如果您费心实际使用您的代码,那么生成的汇编程序会看起来像这样,并且在注意到之后,您一开始就不会问这个问题:

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.23026.0 

    TITLE   E:\R\playground\temp\ConsoleApplication2\ConsoleApplication2.cpp
    .686P
    .XMM
    include listing.inc
    .model  flat

INCLUDELIB OLDNAMES

EXTRN   @__security_check_cookie@4:PROC
PUBLIC  _main
PUBLIC  ?TestC@@YAXXZ                   ; TestC
PUBLIC  ?TestCpp@@YAXXZ                 ; TestCpp
PUBLIC  ?g@@YAXAAUMyTypeC@@@Z               ; g
PUBLIC  ?f@@YAXAAVMyType@@@Z                ; f
PUBLIC  ??0MyType@@QAE@HHH@Z                ; MyType::MyType
; Function compile flags: /Ogtp
;   COMDAT ??0MyType@@QAE@HHH@Z
_TEXT   SEGMENT
_a$dead$ = 8                        ; size = 4
_b$dead$ = 12                       ; size = 4
_c$dead$ = 16                       ; size = 4
??0MyType@@QAE@HHH@Z PROC               ; MyType::MyType, COMDAT
; _this$ = ecx
; File e:\r\playground\temp\consoleapplication2\consoleapplication2.cpp
; Line 9
    mov DWORD PTR [ecx], 0
    mov eax, ecx
    mov DWORD PTR [ecx+4], 1
    mov DWORD PTR [ecx+8], 2
    ret 12                  ; 0000000cH
??0MyType@@QAE@HHH@Z ENDP               ; MyType::MyType
_TEXT   ENDS
; Function compile flags: /Ogtp
;   COMDAT ?f@@YAXAAVMyType@@@Z
_TEXT   SEGMENT
?f@@YAXAAVMyType@@@Z PROC               ; f, COMDAT
; _mt$dead$ = ecx
; File e:\r\playground\temp\consoleapplication2\consoleapplication2.cpp
; Line 17
    ret 0
?f@@YAXAAVMyType@@@Z ENDP               ; f
_TEXT   ENDS
; Function compile flags: /Ogtp
;   COMDAT ?g@@YAXAAUMyTypeC@@@Z
_TEXT   SEGMENT
?g@@YAXAAUMyTypeC@@@Z PROC              ; g, COMDAT
; _mtc$dead$ = ecx
; File e:\r\playground\temp\consoleapplication2\consoleapplication2.cpp
; Line 18
    ret 0
?g@@YAXAAUMyTypeC@@@Z ENDP              ; g
_TEXT   ENDS
; Function compile flags: /Ogtp
;   COMDAT ?TestCpp@@YAXXZ
_TEXT   SEGMENT
?TestCpp@@YAXXZ PROC                    ; TestCpp, COMDAT
; File e:\r\playground\temp\consoleapplication2\consoleapplication2.cpp
; Line 25
    ret 0
?TestCpp@@YAXXZ ENDP                    ; TestCpp
_TEXT   ENDS
; Function compile flags: /Ogtp
;   COMDAT ?TestC@@YAXXZ
_TEXT   SEGMENT
?TestC@@YAXXZ PROC                  ; TestC, COMDAT
; File e:\r\playground\temp\consoleapplication2\consoleapplication2.cpp
; Line 32
    ret 0
?TestC@@YAXXZ ENDP                  ; TestC
_TEXT   ENDS
; Function compile flags: /Ogtp
;   COMDAT _main
_TEXT   SEGMENT
_main   PROC                        ; COMDAT
; File e:\r\playground\temp\consoleapplication2\consoleapplication2.cpp
; Line 37
    xor eax, eax
; Line 38
    ret 0
_main   ENDP
_TEXT   ENDS
END

生成自:

// ConsoleApplication2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

class MyType 
    int _a, _b, _c;
public:
    MyType(int a, int b, int c) : _a(a), _b(b), _c(c) 
    MyType operator+(MyType & op) /* do something */ 
;

struct MyTypeC 
    int a; int b; int c;
;

void f(MyType & mt)  mt; return; 
void g(MyTypeC & mtc)  mtc; return; 

void TestCpp() 
    for (int i = 100000; --i; ) 
        MyType mt(0, 1, 2);
        f(mt);
    


void TestC() 
    for (int i = 100000; --i; ) 
        MyTypeC mtc =  0,1,2 ;
        g(mtc);
    

int main()

    TestCpp();
    TestC();
    return 0;

【讨论】:

除非必要,否则不应包含“stdafx”。这只会造成混乱。 请支持我,我已经在答案上花费了 10 倍的精力来解决这个问题。删除那条线并将设置切换为“不使用...”对我以前的自己来说太过分了;)【参考方案2】:

您是否在观察实际差异?除非您的示例中没有显示隐藏成本,例如虚拟构造函数或方法,否则它们将生成相同的机器代码。在 C++ 中,structclass 之间唯一真正的区别是前者为成员建立了默认的 public 范围,而后者为成员建立了默认的 private 范围。

【讨论】:

这更像是一条评论 成员,基类。

以上是关于将 C++ 类优化为与 C-struct 一样快的主要内容,如果未能解决你的问题,请参考以下文章

将 c-struct 放入 NSArray 的最佳方法是啥?

在 C++ 中,如何将返回值模板化为与参数值不同?

为啥导入的类优先于捆绑类?

如何让 CSS 类优先于 id?

为啥将 C++ 中的整数初始化为 010 与将其初始化为 10 不同?

具有 char 数组的 C++ 结构以不寻常的方式初始化为零