如我所说,我的头文件是:
class A
{
void Complicated();
}
我的源文件
void A::Complicated()
{
...really long function...
}
我可以将源文件拆分为
void DoInitialStuff(pass necessary vars by ref or value)
{
...
}
void HandleCaseA(pass necessary vars by ref or value)
{
...
}
void HandleCaseB(pass necessary vars by ref or value)
{
...
}
void FinishUp(pass necessary vars by ref or value)
{
...
}
void A::Complicated()
{
...
DoInitialStuff(...);
switch ...
HandleCaseA(...)
HandleCaseB(...)
...
FinishUp(...)
}
完全为了可读性而不担心性能方面的影响?
答案 0 :(得分:11)
您应该标记函数static
,以便编译器知道它们是该翻译单元的本地函数。
如果没有static
,编译器就不能假定(禁止LTO / WPA)该函数只被调用一次,因此不太可能内联它。
使用LLVM Try Out页面进行演示。
那就是说,首先是可读性的代码,微优化(以及这样的调整 微观优化)应该只是在性能测量之后。
示例:
#include <cstdio>
static void foo(int i) {
int m = i % 3;
printf("%d %d", i, m);
}
int main(int argc, char* argv[]) {
for (int i = 0; i != argc; ++i) {
foo(i);
}
}
使用static
:
; ModuleID = '/tmp/webcompile/_27689_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-unknown-linux-gnu"
@.str = private constant [6 x i8] c"%d %d\00" ; <[6 x i8]*> [#uses=1]
define i32 @main(i32 %argc, i8** nocapture %argv) nounwind {
entry:
%cmp4 = icmp eq i32 %argc, 0 ; <i1> [#uses=1]
br i1 %cmp4, label %for.end, label %for.body
for.body: ; preds = %for.body, %entry
%0 = phi i32 [ %inc, %for.body ], [ 0, %entry ] ; <i32> [#uses=3]
%rem.i = srem i32 %0, 3 ; <i32> [#uses=1]
%call.i = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str, i64 0, i64 0), i32 %0, i32 %rem.i) nounwind ; <i32> [#uses=0]
%inc = add nsw i32 %0, 1 ; <i32> [#uses=2]
%exitcond = icmp eq i32 %inc, %argc ; <i1> [#uses=1]
br i1 %exitcond, label %for.end, label %for.body
for.end: ; preds = %for.body, %entry
ret i32 0
}
declare i32 @printf(i8* nocapture, ...) nounwind
没有static
:
; ModuleID = '/tmp/webcompile/_27859_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-unknown-linux-gnu"
@.str = private constant [6 x i8] c"%d %d\00" ; <[6 x i8]*> [#uses=1]
define void @foo(int)(i32 %i) nounwind {
entry:
%rem = srem i32 %i, 3 ; <i32> [#uses=1]
%call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str, i64 0, i64 0), i32 %i, i32 %rem) ; <i32> [#uses=0]
ret void
}
declare i32 @printf(i8* nocapture, ...) nounwind
define i32 @main(i32 %argc, i8** nocapture %argv) nounwind {
entry:
%cmp4 = icmp eq i32 %argc, 0 ; <i1> [#uses=1]
br i1 %cmp4, label %for.end, label %for.body
for.body: ; preds = %for.body, %entry
%0 = phi i32 [ %inc, %for.body ], [ 0, %entry ] ; <i32> [#uses=3]
%rem.i = srem i32 %0, 3 ; <i32> [#uses=1]
%call.i = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str, i64 0, i64 0), i32 %0, i32 %rem.i) nounwind ; <i32> [#uses=0]
%inc = add nsw i32 %0, 1 ; <i32> [#uses=2]
%exitcond = icmp eq i32 %inc, %argc ; <i1> [#uses=1]
br i1 %exitcond, label %for.end, label %for.body
for.end: ; preds = %for.body, %entry
ret i32 0
}
答案 1 :(得分:7)
取决于别名(指向该函数的指针)和函数长度(分支中内联的大函数可能会将另一个分支从缓存中抛出,从而影响性能)。
让编译器担心,你担心你的代码:)
答案 2 :(得分:7)
一个复杂的函数可能会以函数内的操作为主;即使它没有内联,函数调用的开销也不会很明显。
你无法控制函数的内联,最好的方法就是尝试并找出函数。
对于较短的代码片段,编译器的优化器可能更有效,因此即使它没有内联,您也可能会发现它变得更快。
答案 3 :(得分:0)
如果将代码拆分为逻辑分组,编译器将执行它认为最好的方法:如果它简单易行,编译器应该内联它并且结果是相同的。但是,如果代码很复杂,那么进行额外的函数调用实际上可能更快而不是执行所有内联工作,因此您也可以选择执行此操作。最重要的是,逻辑上分割的代码对于维护者来说更容易理解并避免将来的错误。
答案 4 :(得分:0)
我建议你创建一个帮助类来将复杂的函数分解为方法调用,就像你提出的那样,但没有漫长,乏味和难以理解的任务,即将参数传递给每个这些较小的函数。通过使这些参数成为辅助类的成员变量,只传递一次这些参数。
此时不要专注于优化,确保您的代码可读,99%的时间都可以。