Macros vs. Inline functions

In my program, I have a small piece of code that would be inconvenient to rewrite every time that I need to use it. Far more useful would be to wrap it in a macro or inline function and call it instead of having to write the whole thing out each time. Functionally speaking, there's no difference between let value = my_code_fragment(arg); and let value = my_code_fragment!(arg); and let value = { /* my code fragment written out */ }

Currently, I'm using macros, but are there any benefits to using inline functions? In C I'd usually write inline functions, but Rust's macros are a lot nicer so I decided to use those. I assume that they produce identical code (but do they?).

Which one is preferred? Are there benefits to using one over the other? Which one compiles faster?

Modern C compilers tend to ignore the inline attribute of functions and regardlessly inline functions if they're short enough so inlining would improve performance. The Rust compiler is backed by the LLVM, the infrastructure powers the clang C compiler, and share the same property. In general, use macro only if it's not possible to do with functions.

3 Likes

During function call, transfer of control takes place

They are totally different language constructs, and they serve different purposes.

You should usually prefer writing and calling functions. Functions are type checked (i.e. they have access to the full power of the type system), and their syntax is fixed and pretty obvious. You can do a lot of things with functions, and if you are concerned about function call overhead, then you probably just shouldn't be, to put it simply.

Macros are not for making code more efficient by eliminating function calls. The optimizer does that anyway. Macros are suitable for abstracting over syntax, when the type system is not smart enough to give you some information about the code. For example, you can't get a list of field names for a struct using a plain function or a trait or anything like that. You'll need a procedural macro (e.g. a custom #[derive]) for that.

However, macros are a purely syntactic construct, so they are expanded long before type checking starts. Therefore, they have no access to the type system. Being able to rewrite syntax is also a potential source of confusion, which can be abused more easily than a plain old function call.

TL;DR: if you don't need a macro, don't use one. Don't use a macro for performing inlining and similar micro-optimizations. Use a macro when you have to do something that a plain function can't do, e.g. metaprogramming.

8 Likes

The use case here is an emulator, and the code fragment in question is critical for performance, being called many tens of millions of times per second. Each instruction is supposed to execute in a matter of nanoseconds (i.e. 30 million instructions per second means that each instruction has around 33ns to run), so the effect of individual function calls really adds up (yes, I've benchmarked -- in C, there's around a 10-15% performance boost in inlining the code, especially since it's pretty short).

Thanks for the advice, and I'm going to start doing that. It makes sense.

Note that you can add the attribute #[inline(always)] to functions that you want to inline. It's not guaranteed to be respected, though, it's only a hint.

1 Like

Though IIRC it is always respected, currently, except for recursive functions.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.