将 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++ 中,struct
是 class
的同义词,但默认情况下具有公共访问权限。
"使用类的原因是需要运算符重载。" - 你可以重载结构体的操作符,就像你可以重载类一样。
旁注:我想知道有多少“铁杆 C”程序员在他们的脑海中有着同样的神话。
【参考方案1】:
你的问题的答案是双重的:
-
您的源代码甚至没有编译,因此我假设您从未运行过代码。因此,您的问题不是基于任何观察。两者都同样快。此外,如果您运行了代码的优化版本,您会看到优化器生成的机器代码几乎等同于两种情况下的 NOP。 IE。它们的速度相同。
struct
和 class
在 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++ 中,struct
和 class
之间唯一真正的区别是前者为成员建立了默认的 public
范围,而后者为成员建立了默认的 private
范围。
【讨论】:
这更像是一条评论 成员,和基类。以上是关于将 C++ 类优化为与 C-struct 一样快的主要内容,如果未能解决你的问题,请参考以下文章
将 c-struct 放入 NSArray 的最佳方法是啥?