"inline" 키워드 vs "인라이닝" 개념
저는 기록을 바로 세우기 위해 이 기본적인 질문을 하는 것입니다.이 질문과 현재 수락된 답변을 참조했는데, 설득력이 없습니다.그러나 두 번째로 많이 투표된 답변은 더 나은 통찰력을 제공하지만, 완벽하지도 않습니다.
.inline
키워드와 "인라이닝" 컨셉.
제 의견은 이렇습니다.
'인라이닝(inlineing)' 개념
이것은 함수의 호출 오버헤드를 저장하기 위해 행해집니다.매크로 스타일의 코드 교체와 더 유사합니다.논쟁할 것 없습니다.
inline
지각 A
inline
키워드(keyword)는 컴파일러가 이를 최적화하고 더 빠른 호출을 할 수 있도록 더 작은 기능에 주로 사용되는 컴파일러에게 요청하는 것입니다.컴파일러는 그것을 무시할 자유가 있습니다.
아래의 이유로 부분적으로 이의를 제기합니다.
- 더 큰 함수는 는 /합니다.
inline
- 는 .
inline
키워드가 언급되는지 여부.
사용자가 합니다의 기능에 명백합니다.inline
.
인식B
inline
인라이닝의 개념과는 상관이 없습니다.놓는 것inline
큰/재귀적 함수 앞에 있는 함수는 도움이 되지 않지만, 작은 함수는 줄을 긋는 데 필요하지 않습니다.유일한 결정론적 용도는
inline
하나의 정의 규칙을 유지하는 것입니다.
,inline
아래 사항만 의무화됩니다.
- 이 여러 에 )(: )
.cpp
파일), 컴파일러는 1개의 정의만 생성하고 다중 심볼 링커 오류를 방지합니다. (참고: 함수의 본문이 다르면 정의되지 않은 동작입니다.) - 몸.
inline
함수를 사용하는 모든 번역 단위에서 볼 수 있어야 합니다.,하는 것,입니다.inline
에서의 기능.h
그리고 어느 한 쪽에 정의를 내리는 것..cpp
하면 다른류 합니다" " 심볼 가 발생합니다..cpp
평결
IMO, "A"라는 인식은 전적으로 잘못된 것이고 "B"라는 인식은 전적으로 옳은 것입니다.
이에 대한 표준적인 인용문이 몇 개 있지만, 이 평결이 맞는지 아닌지 논리적으로 설명하는 답변이 기대됩니다.
Bjarne Strostrup에서 보낸 이메일 답장:
"수십 년 동안 사람들은 컴파일러/옵티마이저가 인간보다 인라인을 더 잘한다고 약속해 왔습니다.이것은 이론적으로는 사실일 수 있지만, 특히 전체 프로그램 최적화가 가능하지 않은 환경에서는 여전히 훌륭한 프로그래머에게는 실제로 적용되지 않습니다.명백한 인라이닝을 신중하게 사용함으로써 얻을 수 있는 중요한 이점이 있습니다."
저는 당신의 주장에 확신이 없었습니다.
더 작은 기능은 인라인 언급 여부에 관계없이 옵티마이저에 의해 자동으로 "인라인"됩니다...가 키워드닝 합니다"하여를 하여 " 제어권도 있지합니다.
inline
.
의 를 할 수 inline
요청은 했지만 완전히 무시한 것 같지는 않았습니다.
Clang과 LLVM을 찾기 위해 Github 저장소를 찾아 보았습니다. (오픈소스 소프트웨어 감사합니다!)키워드를 사용하면 Clang/LLVM이 함수를 인라인화할 가능성이 높다는 것을 알게 되었습니다.
더 서치
를 검색하는 중입니다.inline
Clang 저장소에서 토큰 지정자로 연결됩니다.kw_inline
관련 Clang 렉서 와 것이 그래서 바로 다음과 같은 주목할 만한 것이 있습니다.if (tokenString == "inline") return kw_inline
발견될 겁니다하지만 여기 ParseDecl.cpp에서는kw_inline
다로 됩니다.DeclSpec::setFunctionSpecInline()
.
case tok::kw_inline:
isInvalid = DS.setFunctionSpecInline(Loc, PrevSpec, DiagID);
break;
그 기능 안에서는 비트를 설정하고 중복되는 경우 경고를 표시합니다.inline
:
if (FS_inline_specified) {
DiagID = diag::warn_duplicate_declspec;
PrevSpec = "inline";
return true;
}
FS_inline_specified = true;
FS_inlineLoc = Loc;
return false;
중인 항목:FS_inline_specified
다른 곳에서는 비트 필드에 있는 단일 비트를 볼 수 있고 게터 기능에 사용됩니다.isInlineSpecified()
:
bool isInlineSpecified() const {
return FS_inline_specified | FS_forceinline_specified;
}
의 통화 하는 중isInlineSpecified()
, C++ 파싱 트리를 LLVM 중간 표현으로 변환하는 코드젠을 찾습니다.
if (!CGM.getCodeGenOpts().NoInline) {
for (auto RI : FD->redecls())
if (RI->isInlineSpecified()) {
Fn->addFnAttr(llvm::Attribute::InlineHint);
break;
}
} else if (!FD->hasAttr<AlwaysInlineAttr>())
Fn->addFnAttr(llvm::Attribute::NoInline);
LLVM에 연결
C++ 파싱 단계가 끝났습니다.이제 우리의inline
립 LLVM됩니다의 됩니다.Function
물건.Clang에서 LLVM 저장소로 전환합니다.
중인 항목:llvm::Attribute::InlineHint
합니다를(를) 합니다. Inliner::getInlineThreshold(CallSite CS)
(무서워 보이는 무안경 블록으로):
// Listen to the inlinehint attribute when it would increase the threshold
// and the caller does not need to minimize its size.
Function *Callee = CS.getCalledFunction();
bool InlineHint = Callee && !Callee->isDeclaration() &&
Callee->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
Attribute::InlineHint);
if (InlineHint && HintThreshold > thres
&& !Caller->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
Attribute::MinSize))
thres = HintThreshold;
및 을 긋고 HintThreshold
, (HintThreshold는 명령줄에서 설정 가능)
getInlineThreshold()
하나의 통화 사이트만 있는 것으로 보입니다.SimpleInliner
:
InlineCost getInlineCost(CallSite CS) override {
return ICA->getInlineCost(CS, getInlineThreshold(CS));
}
가상 메소드라고 하는데 이름도 이름이 붙여졌습니다.getInlineCost
의 )를 . .InlineCostAnalysis
.
중인 항목:::getInlineCost()
다을 찾습니다.AlwaysInline
다또 이기도 합니다.InlineCostAnalysis
합니다.Threshold
매개 변수 여기:
CallAnalyzer CA(Callee->getDataLayout(), *TTI, AT, *Callee, Threshold);
bool ShouldInline = CA.analyzeCall(CS);
CallAnalyzer::analyzeCall()
는 200개 이상의 라인이며 함수가 인라인 가능한지 여부를 결정하는 실제 작업을 수행합니다.그것은 많은 요소들에 무게를 가하지만, 우리가 방법을 통해 읽을 때 우리는 그것의 모든 계산이 다음을 조작하는 것을 봅니다.Threshold
eCost
그리고 마지막에:
return Cost < Threshold;
값이 입니다.ShouldInline
정말 잘못된 이름입니다.실의 은.analyzeCall()
설정하는 것입니다.Cost
그리고.Threshold
CallAnalyzer
물건.반환 값은 다음과 같이 다른 요인이 비용 대 임계값 분석을 무시한 경우만 나타냅니다.
// Check if there was a reason to force inlining or no inlining.
if (!ShouldInline && CA.getCost() < CA.getThreshold())
return InlineCost::getNever();
if (ShouldInline && CA.getCost() >= CA.getThreshold())
return InlineCost::getAlways();
않으면 합니다.Cost
그리고.Threshold
.
return llvm::InlineCost::get(CA.getCost(), CA.getThreshold());
그래서 우리는 대부분의 경우 찬성 또는 반대 결정을 하지 않을 것입니다.검색이 계속됩니다!?getInlineCost()
고?
진정한 결정
bool Inliner::shouldInline(CallSite CS)
또 의 큰 또 하나의 큰 기능.은라고 부릅니다.getInlineCost()
그 .getInlineCost
는 함수의 인수 서명, 코드 길이, 재귀, 분기, 연결 등 함수에 대한 고유 비용과 함수가 사용되는 모든 곳에 대한 일부 집계 정보를 분석합니다.반면에.shouldInline()
는 이 정보를 함수가 사용되는 특정 장소에 대한 더 많은 데이터와 결합합니다.
에 걸쳐 에 .InlineCost::costDelta()
-합니다.InlineCost
s Threshold
한 값analyzeCall()
. 마침내, 우리는 a를 돌려줍니다.bool
. 결정되었습니다. 인.Inliner::runOnSCC()
:
if (!shouldInline(CS)) {
emitOptimizationRemarkMissed(CallerCtx, DEBUG_TYPE, *Caller, DLoc,
Twine(Callee->getName() +
" will not be inlined into " +
Caller->getName()));
continue;
}
// Attempt to inline the function.
if (!InlineCallIfPossible(CS, InlineInfo, InlinedArrayAllocas,
InlineHistoryID, InsertLifetime, DL)) {
emitOptimizationRemarkMissed(CallerCtx, DEBUG_TYPE, *Caller, DLoc,
Twine(Callee->getName() +
" will not be inlined into " +
Caller->getName()));
continue;
}
++NumInlined;
InlineCallIfPossible()
합니다를 .shouldInline()
의 결정.
.Threshold
다의 을 받았습니다inline
키워드이며, 인라인 여부를 결정할 때 마지막에 사용됩니다.
가 B 는 를 에 당신의 왜냐하면 적어도 하나의 주요 컴파일러가 그것의 최적화 동작을 다음을 기반으로 바꾸기 때문입니다.inline
키워드.
그 도 볼 수.inline
힌트일 뿐이고 다른 요인이 더 클 수도 있습니다.
둘 다 맞습니다.
의 .inline
함수에 대한 특정 호출을 인라인화하는 컴파일러의 결정에 영향을 미칠 수도 있고 그렇지 않을 수도 있습니다.따라서 A가 맞습니다. 컴파일러가 무시할 수 있는 인라인 함수를 호출하는 구속력 없는 요청으로 작용합니다.
의 .inline
는 하나의 정의 규칙(One Definition Rule)의 제한을 완화하여 B에 설명된 것처럼 동일한 정의를 여러 번역 단위로 허용하는 것입니다.많은 컴파일러의 경우 함수 호출의 인라인을 허용하기 위해 이 기능이 필요합니다. 정의는 해당 시점에서 사용할 수 있어야 하며 컴파일러는 한 번에 하나의 번역 단위만 처리하면 됩니다.
언급URL : https://stackoverflow.com/questions/27042935/inline-keyword-vs-inlining-concept
'programing' 카테고리의 다른 글
경고 NETSDK1071 'Microsoft'에 대한 패키지 참조.AspNetCore.'앱'은 '2.1.6' 버전을 지정했습니다. (0) | 2023.10.13 |
---|---|
지정된 값이 열에 있는 행에서 여러 개의 서로 다른 값을 가져오는 Mysqlike (0) | 2023.10.13 |
Chrome에서 JavaScript 메모리 누수 찾기 (0) | 2023.10.13 |
Larvel에서 수동 설정으로 데이터베이스와 연결할 수 없습니다. (0) | 2023.10.13 |
워드프레스 폼취급 (0) | 2023.10.13 |