7 ms·
I have always wondered why functions have to be declared inline when it is more up to the caller to declare whether to inline the contents of the function at ca
by snidane 6y ago
I have always wondered why functions have to be declared inline when it is more up to the caller to declare whether to inline the contents of the function at call site.
So instead of
inline int f(int x) { return x * x; }
int main() { f(3); }
you'd want to do
int f(int x) { return x * x; }
int main() { inline f(3); }
I have seen this implemented in Zig for loops (inline for, inline while). Is there support for this in other languages?
- patrec 6y agoYes, Common Lisp does this right, see: http://clhs.lisp.se/Body/d_inline.htm http://clhs.lisp.se/Body/d_inline.htm
- thelopa 6y agoSort of. I think CL has the right direction but the details are wrong. This pattern requires declaring the function notinline, and that is a much stronger statement than just “wait I take it back”. Using this pattern, the function can ONLY be inlined in the contexts where you allow it. Similarly, compiler macros are disabled for notinline functions. I would like the compiler to use its normal judgement for inlining and continue using compiler macros for call sites where I don’t provide an inline declaration.
- patrec 6y agoI think you overlook that inlining in common lisp is a lot less trivial than in very static languages like C++, because it is perfectly valid to redefine functions at runtime. This does obviously not play well with the compiler randomly inlining things behind your back. You can fix that by keeping track of where you inlined f and then re-compile all the inlined sites as well, but that obviously has significant pathologies as well. Having said that, performance oriented common lisp compilers like sbcl (and its predecessor) will generally allow you to block-compile stuff which will get rid of the extra indirections at the cost of less runtime flexibility. IIRC that will also mean the compiler will use its judgement whether to inline a function.
- kazinator 6y agoCommon Lisp has pretty clear, straightforward rules about inlining. File compilation is allowed to automatically inline function calls that are in the same compilation unit as their target. This follows from the reasoning that material which is compiled together will be loaded together, and those functions will be redefined together. The details are in 3.2.2.3 Semantic Constraints, bullets 3-5. In any case where this inlining would be a problem, you must use declaim globally or declare locally to request notinline. Likewise, you can use an inline declaration to encourage inlining.
- patrec 6y agoYes, the standard allows this, but that doesn't mean inlining is straightforward, in the sense that the compiler can't just magically figure out unaided whether to inline stuff or not and all is well as the GP wanted. Whether two functions were defined in the same file has basically zero bearing on the desirability of inlining, so the fact that recompiling a whole file rather than an individual function is not particuarly onerous doesn't really help that much. Also, correct me if I'm wrong, but I don't think anything that sbcl does if you pass :block-compile t goes beyond what would already be allowed by the standard as default (by contrast, CMUCL's block compilation supported multiple files and thus essentially also whole-program optimization). The fact that it's not the default (despite likely non-trivial performance benefits) probably indicates that a majority of users would find this behavior problematic as default, regardless of whether it is standard conformant.
- lispm 6y ago> that the compiler can't just magically figure out unaided whether to inline stuff or not Allegro CL does inlining on its own, IIRC. > majority of users would find this behavior problematic as default Block compilation or whole program compilation are used for delivery of applications.
- patrec 6y ago> Block compilation or whole program compilation are used for delivery of applications. Mostly, sure just like LTO in C/C++. But if I were doing any scientific programming with common lisp I'd try to use block compilation in a few select places during interactive development as well. I'd imagine that for some code this would get rid of a lot of boxing and unboxing as well as type checks, in addition to getting rid of indirection. I'd not be surprised if you could get an integer factor speed up if you got many small functions which mostly operate on doubles or double arrays. I should probably give it a try just to satisfy my curiosity.
- pjmlp 6y agoModifiers like inline and register are left overs from the days the compilers had to work in 64KB or less and needed every help they could get from the developers. Nowadays most optimisers ignore them anyway and use heuristics to decide if and when they actually care, and also inline when not asked to. If you really want the original behaviour you need to use non standard modifiers like forceinline.
- vkazanov 6y agoCarmack was talking about a different kind of inline :-)
- lubonay 6y agoYeah, I believe the point was to inline source code so it's easier to reason about as you read it, not to inline at compilation for some runtime advantage.
- wahern 6y agoIn C this is because the inline qualifier has at least as much to do with linking and controlling symbol definitions as it does literal inlining of the code into the caller. Relatedly, in languages like C that have global mutable state, it's the callee who knows better whether and when it's safe to inline code, not the caller. Ditto in languages (like C) that can have complex linking semantics, multiple function definitions, etc.
- gpderetta 6y agoInline in c++ doesn't do what many people think it does, i.e the keyword is neither necessary nor sufficient for performing the inlining optimization (although the compiler might treat it as a weak hint). Instead is a directive to the compiler to ignore the One Definition Rule for the function and it is an artefact of the header inclusion model of compilation.
- bjoli 6y agoIn a language like guile scheme or racket this could be implemented as a macro. In guile, the (define-inlinable ...) Is already defined as a macro, and while doing it in reverse would be harder it could be done and tested in an hour. I would probably do it by overriding (define ...) to make it also store the source of the function somewhere and make (inline ...) just insert it as an anonymous function: (define (square x) (* x x)) (inline square 5) The last call would be expanded to ((lambda (x) (* x x) 5) Which the guile inliner always in-lines.
- Kiro 6y agoCarmack is talking about removing the function call altogether in the code. It has nothing to do with the inline keyword.