7 ms·
Usual disclaimer of profile your code to identify areas for improvement and try to quantify changes as far as possible -- it's no good doing things just because
by aglionby 9y ago
Usual disclaimer of profile your code to identify areas for improvement and try to quantify changes as far as possible -- it's no good doing things just because you think it'll make the program faster.
Specifically on the Java side of things, while not quite as low-level as what the post is talking about, I've seen situations where proper use of logging libraries has given perf increases. It's as simple as log.info("Foo: {}", "bar") instead of log.info(String.format("Foo: %s", "bar")) -- reducing the number of string operations dependant on your chosen logging level. Simple but I've seen it happen.
Shoutout to Brandon Gregg's blog [0] with some interesting stuff on performance, some of which is Java-specific.
[0] http://www.brendangregg.com/ http://www.brendangregg.com/
- _old_dude_ 9y agoyes, String.valueOf() is dog slow. We recently moved all logging stuff to use a constant lambda as first parameter, log.info(s -> "Foo: " + s, "bar") so if the logger is not enabled, the constants are discarded by the VM and otherwise the string concatenation is faster than any string interpolations.
- sk5t 9y agoHave you measured this? I would expect the allocation of Callable<String> with closures to be slightly worse than common types of string concatenation. Not to mention the awkwardness of only logging effectively final vars--this would be a red flag on organizational coding practices--just use slf4j or better yet Scala + LazyLogging.
- _old_dude_ 9y agoIf you use a closure that capture some local variables and not a constant lambda, then you're right, the allocation will kill you. But, we are only using lambdas that do not capture anything so the lambda is allocated once by the VM. And we are using the new shiny System.Logger [1] so the VM logs and the application logs are interleaved. We still have some wrappers to redirect log4j logs on the system logger because few third party libs are compatible with the system logger. [1] https://docs.oracle.com/javase/9/docs/api/java/lang/System.Logger.html https://docs.oracle.com/javase/9/docs/api/java/lang/System.L...
- sk5t 9y agoAh, makes sense. On the other hand, this would seem to take a StringBuilder-based approach off the table (cf. logback). I wonder how this stacks up to simply passing varargs to the logger. There's also the question of applying any strategy other than toString to the non-string args.
- specialist 9y agoHuh. I'll have to try that. Thanks. My solution was to flip the problem, making a separate logger instance for each log level. So something like Logger warn = Logger.factory(Level.WARN); warn.log( "message" ); If warn is disabled, factory returns a NullObject (do nothing) logger. (My actual implementation had one level of indirection, so logs levels could be updated at runtime.) Then I got nutty optimizing printf, doing JIT class generation per template... But that's another story. TL;DR: Logging in Java drives me nuts, so I made my own.
- _old_dude_ 9y agoDid you take a look to java.lang.invoke.StringConcatFactory, it does more or less what you want using method handles instead of bytecode generation ?
- specialist 9y agoThanks for the tip. Will check it out. I'll be happy if it moots my hackery.
- deleted 9y ago[deleted]
- cbsmith 9y agoWait, your string literals are still going to get intern'd, regardless of whether they are in a Lambda or not.
- cbsmith 9y agoString formatting in general will be the death of all performance... and humanity.
- reader_1000 9y agoWell, since Java evaluates method arguments all the time, if you have a computationally-heavy argument, then it is better to check debug level: if(log.isInfoEnabled()) { log.info("Foo: {}", someHeavyOperationThatReturnsString()); }