When should inline not to be used?

I found, using @inline in some situation can make 10 times more allocation than that without using it. I wonder why this happen?

Hi. An example would make the wheels turn faster :slight_smile:

2 Likes

I don’t think that should ever happen. Conversely, when a function is not inlined, it’s also undesirable, I’m scratching my brain to see if it could happen then. In general you can’t easily know if your functions will be inlined, it might change with some small change.

Did you make a mistake, and only saw allocations on first use?

Basically never use @inline unless you know exactly why you are doing it. There are major potential problems with it, e.g.: dont @inline large constants by rafaqz · Pull Request #114 · JuliaData/Parsers.jl · GitHub

Its mostly useful when you have a chain of small functions you are sure you need to compile away. Otherwise let the compiler decide.

(But also, those problems don’t usually involve allocations - that doesn’t really make sense to me)

6 Likes

Another example (adapted from a 2016 Github comment by Ian Lance Taylor):

function internal()
  # Some large and complex function body goes here
end

function external()
  very_rare_case() && internal()  # The only place where `internal` is used.
end

In this case it wouldn’t make sense to inline internal into external, even though internal isn’t used anywhere else.
This is because when internal isn’t inlined, internal’s huge stack frame doesn’t need to be set up except for the rare case when internal actually gets called.

2 Likes

Basically never use @inline unless you know exactly why you are doing it.

True, on (as opposed to within) function bodies, but new in 1.8:

The usage within a function body requires at least Julia 1.8.

I guess you shouldn’t bother either, unless you know what you’re doing, I believe I do, and know @inline and @noinline, for it also:

The usage within a function body requires at least Julia 1.8.

It feels though within less dangerous for regular users to use.

All this got me curious what C, C++ (and other languages, Rust) do. I knew the compilers inline, but didn’t recall the inline keyword, and new meaning in C++17:

https://en.cppreference.com/w/cpp/language/inline

In Julia it’s about functions, and I think similar. On variables, is new in C++17, Julia doesn’t have anything similar (or need?)?

1 Like

The C++ inline isn’t about inlining. Search the page you linked for “inlining”, and the only hit is:

Because the meaning of the keyword inline for functions came to mean “multiple definitions are permitted” rather than “inlining is preferred”, that meaning was extended to variables.

1 Like

I think you’re wrong about C++ ([EDIT: You might be right about the standard but wrong about some implementations such as Microsoft’s?] the keyword just has more than one meaning there, depending on context), at least I see (and the text there is helpful not just for C+, but also for Julia on the general concept):

inline, __inline, and __forceinline

The inline and __inline specifiers instruct the compiler to insert a copy of the function body into each place the function is called.

The insertion, called inline expansion or inlining, occurs only if the compiler’s cost-benefit analysis shows it’s worthwhile. Inline expansion minimizes the function-call overhead at the potential cost of larger code size.

The __forceinline keyword overrides the cost-benefit analysis and relies on the judgment of the programmer instead.

I think Julia’s inline corresponds to Rust’s #[inline(always)], which isn’t “always”… there, or I think in Julia (in both just a strong suggestion):
https://nnethercote.github.io/perf-book/inlining.html

There are four inline attributes that can be used on Rust functions.

  • None. The compiler will decide itself if the function should be inlined. This will depend on the optimization level, the size of the function, etc. If you are not using link-time optimization, functions will never be inlined across crates.
  • #[inline]. This suggests that the function should be inlined, including across crate boundaries.
  • #[inline(always)]. This strongly suggests that the function should be inlined, including across crate boundaries.
  • #[inline(never)]. This strongly suggests that the function should not be inlined.

Inline attributes do not guarantee that a function is inlined or not inlined, but in practice, #[inline(always)] will cause inlining in all but the most exceptional cases.

It’s rather amazing what’s possible with this crate (see example Rust/C code): inline_c - Rust [Somewhat similar to Cxx.jl that I miss in Julia]

inline-c is a small crate that allows a user to write C (including C++) code inside Rust. Both environments are strictly sandboxed: it is non-obvious for a value to cross the boundary. The C code is transformed into a string which is written in a temporary file. This file is then compiled into an object file, that is finally executed. It is possible to run assertions about the execution of the C program.