bsideup / groovy-macro-methods-proposal

Apache Groovy proposal of macro methods implementation
Apache License 2.0
9 stars 0 forks source link

Comparison to JIT inlining #3

Open gillius opened 8 years ago

gillius commented 8 years ago

It seems to me that most users will use macro methods for simple cases, like the "safe" example from https://github.com/bsideup/groovy-macro-methods#usage. From what I understand, modern JVM JIT compiler can do inlining of methods, and in the case of simple branching, JITs can do profile-based optimizations if it detects a "dead" branch, for example if you have a static final boolean development = ... boolean, can the JIT remove the branch entirely from a if (development) { log } method, which could perform just as well but allow you to change behavior after compile time?

A potential route to take is a JMH benchmark of a logging-type operation based on a static final, to demonstrate the amount of difference, if it is positive then it is a good selling point for macro methods. If the difference is zero, then it can be an advice to avoid simple macros and keep to the powerful cases like the ORM example.

Or, perhaps even barring a performance difference, it could still be considered a stronger form of the compile-time optimization that https://github.com/EsotericSoftware/minlog uses, especially if the compiler ends up even removing the need for a separate library (i.e. use a compile-time flag to remove logging calls, now you don't even need logging library on classpath!)

bsideup commented 8 years ago

@gillius actually, "safe" example is not that simple as you think. Note that there is no "?." operator in a call chain, so you can write:

def value = safe(my.object.property[0].value)

and it will be transformed to something like:

def value = my?.object?.property[0]?.value

automatically.

Regarding "development" stuff - again, without some code pre-processor you can't (at compile time) determine if it's development or not.

Your example with if (development) {} is a good example - how would you get development variable? Because it's not a constant, it depends on the environment on compile time instead of runtime.

And yes, "macro methods"-based logging would be something really cool! With Macro Methods you can also analyze passed AST and do some compile time magic as well. i.e.

User user = ...

printProps(user)

Since User type will be available at compile time, it can determine the properties of User class and generate code like:

User user = ...

println("User(name=${user.name}, age=${user.age})")

Even if toString method of User is not implemented.

gillius commented 8 years ago

I didn't realize the safe method was that complex, I think the example you wrote here would be perfect to put into the README, when I saw it, I thought it was an example of re-implementing ?: operator. Now looking at the usage, there's no way ?: would make sense in that context. If I had seen the "before" and "after" code it would make sense right away.

As for the "if development" example, I was thinking of the standard way to do it in Java would be to put it in a properties file in the jar and/or reference a system property, but in all cases the result is at runtime a static final boolean field. The question is, I've heard (but not seen with my own eyes) that the JIT is smart enough to see a static final boolean and "inline" that into the method so it actually can eliminate the impossible code, losing the benefit of the macro transform (because JIT removes the logging statements). However, in cases like your printProps or ORM examples the benefit is very clear.

bsideup commented 8 years ago

@gillius you're right, JIT is smart enough to do "dead code elimination", and, in that case, it should work.

So, you're absolutely right that Macro Methods are useful only where they are useful, and JVM runtime is powerful enough to handle everything else! :)

gillius commented 8 years ago

Yes, so to get to the core suggestion, if it is proven that JIT can do "dead code elimination" then when documenting it could be good to mention this, because when devs like me see "macro" I think of a macro like in C that are usually very simple like a logging statement like #define LOG(x) (println(x)) inside of an "#ifdef" or something like #define IS_READ_FLAG_SET(x) (x >> 3 & 0x1), but with JIT inlining and dead code elimination I suggest we can recommend to user, use normal methods instead of macros for that because JIT does it for you, instead focus on advanced logging, ORM, etc.