Jess - Performance regression from Java 11 to Java 13+ - java

we are using Jess, which is a rules engine, written in Java. It has become pretty old and most of it is written using Java 1.5 and lower language features.
Now, we are using this for many years and never noticed any problems during the Java upgrades, until now, when we plan to switch from Java 11 to Java 17.
Our runtime has become 30-40% slower, just by switching the java version. We did a lot of testing already, but cannot really find the one culprit.
For instance, we checked different Java versions and different vendors (eclipse, azul and orcacle), everything from 13+ was slower than Java 11.
Using VisualVM I was able to point down to a few methods, but there is nothing specific in there where I would say that this could be the problem.
Looking at it, it seems like there are multiple performance regressions that sum together to a big one, but that seems pretty unrealistic.
Some of the functions look like this:
private synchronized Map findVariable(String key) {
Context c = this;
while (c != null) {
Map ht = c.getVariables();
Value v = (Value) ht.get(key);
if (v != null)
return ht;
else
c = c.m_parent;
}
return null;
}
private synchronized Map getVariables() {
if (m_variables == null)
m_variables = Collections.synchronizedMap(new HashMap());
return m_variables;
}
Do you know if there are any know regressions going on from J13+, maybe regarding synchronized functions and variables for instances?
Any other pointers what might be the problem?

Based on the fact the library you are using was written for Java 1.5, that time we were primarily using synchronized keyword to write thread-safe code, I would say the root cause of performance degradation you are observing now is JEP 374: Deprecate and Disable Biased Locking

Related

Java: does lambda usage has negative influence on performance or memory use [duplicate]

This question already has an answer here:
Java lambdas 20 times slower than anonymous classes
(1 answer)
Closed 2 years ago.
Does using lambdas in Java, e.g. when invoking collection.removeif(item -> item.isOptionSet()), has any noticable negative impact on performance or memory usage compared to writing it explicitly
for (Iterator it = collection.iterator(); it.hasNext();) {
if (it.next().isOptionSet()) {
it.remove();
}
}
From a performance/memory point of view, is the above lambda similar to
collection.removeIf(new Predicate<Item>() {
#Override
public boolean test(Item item) {
return item.isOptionSet();
}
});
?
In terms of memory not much difference.
In terms of performance, it all depends on the level of optimization that is performed by the JIT compiler, which depends on many things, such as the Jvm being used, the Jvm version, and the frequency and count of how often the code is run. In the early days of lambdas they were slower because Jvm developers had not yet been able to optimize their performance. Even today I would guess that the loop is fastest, then the anonymous class, then the lambda, but as I said with enough optimization in the compiled code (method inclining, stack allocation, etc) you may end up with the same thing.

Is Objects.requireNonNull less efficient than the old way?

Since JDK 7 I've been happily using the method it introduced to reject null values which are passed to a method which cannot accept them:
private void someMethod(SomeType pointer, SomeType anotherPointer) {
Objects.requireNonNull(pointer, "pointer cannot be null!");
Objects.requireNonNull(anotherPointer, "anotherPointer cannot be null!");
// Rest of method
}
I think this method makes for very tidy code which is easy to read, and I'm trying to encourage colleagues to use it. But one (particularly knowledgeable) colleague is resistant, and says that the old way is more efficient:
private void someMethod(SomeType pointer, SomeType anotherPointer) {
if (pointer == null) {
throw new NullPointerException("pointer cannot be null!");
}
if (anotherPointer == null) {
throw new NullPointerException("anotherPointer cannot be null!");
}
// Rest of method
}
He says that calling requireNonNull involves placing another method on the JVM call stack and will result in worse performance than a simple == null check.
So my question: is there any evidence of a performance penalty being incurred by using the Objects.requireNonNull methods?
Let's look at the implementation of requireNonNull in Oracle's JDK:
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
So that's very simple. The JVM (Oracle's, anyway) includes an optimizing two-stage just-in-time compiler to convert bytecode to machine code. It will inline trivial methods like this if it can get better performance that way.
So no, not likely to be slower, not in any meaningful way, not anywhere that would matter.
So my question: is there any evidence of a performance penalty being incurred by using the Objects.requireNonNull methods?
The only evidence that would matter would be performance measurements of your codebase, or of code designed to be highly representative of it. You can test this with any decent performance testing tool, but unless your colleague can point to a real-world example of a performance problem in your codebase related to this method (rather than a synthetic benchmark), I'd tend to assume you and he/she have bigger fish to fry.
As a bit of an aside, I noticed your sample method is a private method. So only code your team is writing calls it directly. In those situations, you might look at whether you have a use case for assertions rather than runtime checks. Assertions have the advantage of not executing in "released" code at all, and thus being faster than either alternative in your question. Obviously there are places you need runtime checks, but those are usually at gatekeeping points, public methods and such. Just FWIW.
Formally speaking, your colleague is right:
If someMethod() or corresponding trace is not hot enough, the byte code is interpreted, and extra stack frame is created
If someMethod() is called on 9-th level of depth from hot spot, the requireNonNull() calls shouldn't be inlined because of MaxInlineLevel JVM Option
If the method is not inlined for any of the above reasons, argument by T.J. Crowder comes into play, if you use concatenation for producing error message
Even if requireNonNull() is inlined, JVM wastes time and space for performing this.
On the other hand, there is FreqInlineSize JVM option, which prohibits inlining too big (in bytecodes) methods. The method's bytecodes is counted by themselves, without accounting size of methods, called within this method. Thus, extracting pieces of code into independent methods could be useful sometimes, in the example with requireNonNull() this extraction is made for you already.
If you want evidence ... then the way to get it is to write a micro-benchmark.
(I recommend looking at the Calliper project first! Or JMH ... per Boris's recommendation. Either way, don't try and write a micro-benchmark from scratch. There are too many ways to get it wrong.)
However, you can tell your colleague two things:
The JIT compiler does a good job of inlining small method calls, and it is likely that this will happen in this case.
If it didn't inline the call, the chances are that the difference in performance would only be a 3 to 5 instructions, and it is highly unlikely that it would make a significant difference.
Yes, there is evidence that the difference between manual null check and Objects.requireNonNull() is negligible. OpenJDK commiter Aleksey Shipilev created benchmarking code that proves this while fixing JDK-8073479, here is his conclusion and performance numbers:
TL;DR: Fear not, my little friends, use Objects.requireNonNull.
Stop using these obfuscating Object.getClass() checks,
those rely on non-related intrinsic performance, potentially
not available everywhere.
Runs are done on i5-4210U, 1.7 GHz, Linux x86_64, JDK 8u40 EA.
The explanations are derived from studying the generated code
("-prof perfasm" is your friend here), the disassembly is skipped
for brevity.
Out of box, C2 compiled:
Benchmark Mode Cnt Score Error Units
NullChecks.branch avgt 25 0.588 ± 0.015 ns/op
NullChecks.objectGetClass avgt 25 0.594 ± 0.009 ns/op
NullChecks.objectsNonNull avgt 25 0.598 ± 0.014 ns/op
Object.getClass() is intrinsified.
Objects.requireNonNull is perfectly inlined.
where branch, objectGetClass and objectsNonNull are defined as follows:
#Benchmark
public void objectGetClass() {
o.getClass();
}
#Benchmark
public void objectsNonNull() {
Objects.requireNonNull(o);
}
#Benchmark
public void branch() {
if (o == null) {
throw new NullPointerException();
}
}
Your colleague is most likely wrong.
JVM is very intelligent and will most likely inline the Objects.requireNonNull(...) method. The performance is questionable but there will be definitely much more serious optimizations than this.
You should use the utility method from JDK.
Effective Java by Joshua Bloch
Item 67: Optimize judiciously
There are three aphorisms concerning optimization that everyone should know:
More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason—including blind stupidity.
—William A. Wulf [Wulf72]
We should forget about small efficiencies, say about 97% of the time:
premature optimization is the root of all evil.
—Donald E. Knuth [Knuth74]
We follow two rules in the matter of optimization:
Rule 1. Don’t do it.
Rule 2 (for experts only). Don’t do it yet—that is, not until you
have a perfectly clear and unoptimized solution.
—M. A. Jackson [Jackson75]
Meh, No. But, yes.
No, the direct code is always better since the method stack does not need to be touched.
Yes, if the VM implementation has null-check skips or some optimized null-checks.
Meh, method stack is so light to be modified and updated (yet it will consume some time).
As a general rule, readability and maintainability should trump optimization.
This rule safeguards against speculative optimization from people who think they know how a compiler works even though they have never even attempted to write one and they have never had a look inside one.
Your colleague is wrong unless they prove that the performance penalty is noticeable and untenable for users.
Objects.requireNonNull is more optimised as if you this you are code reusability.
Also in oracle requireNonNull si defined as
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
so its already in bytecode.

Why need final def in poll() method of LinkedList in Java

/**
* Retrieves and removes the head (first element) of this list.
*
* #return the head of this list, or {#code null} if this list is empty
* #since 1.5
*/
public E poll() {
final Node<E> f = first; //why final here? <<-------- ******* --------
return (f == null) ? null : unlinkFirst(f);
}
Hi there, I'm reading the source code of JDK 1.7. In above code snippet in LinkedList.java, I cannot understand why need 'final' in poll() method. Why not :
public E poll() {
return (first == null) ? null : unlinkFirst(first);
}
Can you share the insight of the implementation? Thanks.
Most of the methods in LinkedList use the final declaration on local variables.
LinkedList JDK 1.7 Source
This is likely related to the concept behind Using "final" modifier whenever applicable in java.
Adding final to all things which should not change simply narrows down the possibilities that you (or the next programmer, working on your code) will misinterpret or misuse the thought process which resulted in your code. At least it should ring some bells when they now want to change your previously immutable thing.
Technically, at the cost of 6 letters, you guarantee that something you don't ever expect to change will never change.
Does your proposed code work? Yes. I don't see any scenarios where it wouldn't. It is programmatically valid.
However, the use of final throughout the code supports sanity testing, and understandably, for all the util stuff that holds pretty much all of the things we do in Java together, it'd be nice to know that everything is working as intended.
Note: If there is a security issue that I have not seen, however, I would be interested to know about that, in a separate answer.
Martin Buchholz answered this on the concurrency-interest list in a related question:
We in jsr166-land consider our software important enough to make
optimizations we don't recommend to regular java programmers. Copying final
fields to locals generates smaller bytecode and might help the jit produce
better code (and with current hotspot, still does).
Using final on locals has no performance advantage, but it does have some
software engineering advantages. We tend to use it for locals with the same
name as a field, e.g.
final Foo foo = this.foo;
Compass answer is very reasonable, but I just want to add a further guess. I think it's a micro optimization since the access to the local variable f should, on average, be faster than access to the class field first. The final modifier is good practice but doesn't effect access latency. In this specific case the gain would likely be extremely small since you are trading two class field accesses for a single class field access and two local variable accesses.
This is not something that you should do in everyday programming, but since the collections library is used by basically every single Java program in existence these things are justifiable here.

Overwriting vs. Lookup

I was reading through the SparseArray class in android, and came across the following method:
public void removeAt(int index) {
if (mValues[index] != DELETED) {
mValues[index] = DELETED;
mGarbage = true;
}
}
Clearly this could as well has been written:
public void removeAt(int index) { Or public void removeAt(int index) {
if (mValues[index] != DELETED) { mValues[index] = DELETED;
mValues[index] = DELETED; mGarbage = true;
if (!mGarbage) }
mGarbage = true;
}
}
It would seem the android developers believed the array lookup mValues[index] was faster than an array write, but the variable lookup wasn't faster than a variable write.
Is this really true? Does it depend on the VM, or is it general knowledge in compiled languages too?
Certainly the right-hand side version is not equivalent - because then mGarbage is set to true whether or not the value has changed.
The left-hand side is equivalent to the original, but it's pointless.
Basically I think you've missed the side-effect of checking whether or not the existing value was allows DELETED: it allows mGarbage to be set to true only if the method has actually had an effect. That has nothing to do with the performance of reading from the array.
It depends a lot on the VM and I'd guess that this specific code is tuned for the Dalvik VM (or it's just whatever Apache Harmony happened to implement).
One thing to remember is that a write always implies some cost related to caching and cross-thread interaction (i.e. you might need memory barriers for it to work correctly), while a read is much easier to do.
The assumption is probably true, although it will depend a lot on the processor and JVM implementation.
The general reason is less to do with arrays vs. variables but more to do with memory access patterns:
mGarbage is very likely to be locally cached if it's a field value of the current object, either in a register or L1 cache. You probably just rest the object into cache in order to do something like a virtual method lookup a few cycles ago. There won't be much difference between a read or a write when something is locally cached.
mValues[index] is an array lookup that is less likely to be locally cached (particularly if the array is large or only gets accessed sporadically). Reads from non-local caches will usually be faster than writes because of locking / memory contention issues so it makes sense to do a read only if you can get away with it. This effect becomes stronger the more cores you have in your machine and the more concurrency you have in your code.

What's the best way to handle coexistence of the "int enum" pattern with java enums as an API evolves?

Suppose you're maintaining an API that was originally released years ago (before java gained enum support) and it defines a class with enumeration values as ints:
public class VitaminType {
public static final int RETINOL = 0;
public static final int THIAMIN = 1;
public static final int RIBOFLAVIN = 2;
}
Over the years the API has evolved and gained Java 5-specific features (generified interfaces, etc). Now you're about to add a new enumeration:
public enum NutrientType {
AMINO_ACID, SATURATED_FAT, UNSATURATED_FAT, CARBOHYDRATE;
}
The 'old style' int-enum pattern has no type safety, no possibility of adding behaviour or data, etc, but it's published and in use. I'm concerned that mixing two styles of enumeration is inconsistent for users of the API.
I see three possible approaches:
Give up and define the new enum (NutrientType in my fictitious example) as a series of ints like the VitaminType class. You get consistency but you're not taking advantage of type safety and other modern features.
Decide to live with an inconsistency in a published API: keep VitaminType around as is, and add NutrientType as an enum. Methods that take a VitaminType are still declared as taking an int, methods that take a NutrientType are declared as taking such.
Deprecate the VitaminType class and introduce a new VitaminType2 enum. Define the new NutrientType as an enum. Congratulations, for the next 2-3 years until you can kill the deprecated type, you're going to deal with deprecated versions of every single method that took a VitaminType as an int and adding a new foo(VitaminType2 v) version of each. You also need to write tests for each deprecated foo(int v) method as well as its corresponding foo(VitaminType2 v) method, so you just multiplied your QA effort.
What is the best approach?
How likely is it that the API consumers are going to confuse VitaminType with NutrientType? If it is unlikely, then maybe it is better to maintain API design consistency, especially if the user base is established and you want to minimize the delta of work/learning required by customers. If confusion is likely, then NutrientType should probably become an enum.
This needn't be a wholesale overnight change; for example, you could expose the old int values via the enum:
public enum Vitamin {
RETINOL(0), THIAMIN(1), RIBOFLAVIN(2);
private final int intValue;
Vitamin(int n) {
intValue = n;
}
public int getVitaminType() {
return intValue;
}
public static Vitamin asVitamin(int intValue) {
for (Vitamin vitamin : Vitamin.values()) {
if (intValue == vitamin.getVitaminType()) {
return vitamin;
}
}
throw new IllegalArgumentException();
}
}
/** Use foo.Vitamin instead */
#Deprecated
public class VitaminType {
public static final int RETINOL = Vitamin.RETINOL.getVitaminType();
public static final int THIAMIN = Vitamin.THIAMIN.getVitaminType();
public static final int RIBOFLAVIN = Vitamin.RIBOFLAVIN.getVitaminType();
}
This allows you to update the API and gives you some control over when to deprecate the old type and scheduling the switch-over in any code that relies on the old type internally.
Some care is required to keep the literal values in sync with those that may have been in-lined with old consumer code.
Personal opinion is that it's probably not worth the effort of trying to convert. For one thing, the "public static final int" idiom isn't going away any time soon, given that it's sprinkled liberally all over the JDK. For another, tracking down usages of the original ints is likely to be really unpleasant, given that your classes will compile away the reference so you're likely not to know you've broken anything until it's too late
(by which I mean
class A
{
public static final int MY_CONSTANT=1
}
class B
{
....
i+=A.MY_CONSTANT;
}
gets compiled into
i+=1
So if you rewrite A you may not ever realize that B is broken until you recompile B later.
It's a pretty well known idiom, probably not so terrible to leave it in, certainly better than the alternative.
There is a rumor that the creator of "make" realized that the syntax of Makefiles was bad, but felt that he couldn't change it because he already had 10 users.
Backwards compatibility at all costs, even if it hurts your customers, is a bad thing. SO can't really give you a definitive answer on what to do in your case, but be sure and consider the cost to your users over the long term.
Also think about ways you can refactor the core of your code will keeping the old integer based enums only at the outer layer.
Wait for the next major revision, change everything to enum and provide a script (sed, perl, Java, Groovy, ...) to convert existing source code to use the new syntax.
Obviously this has two drawbacks:
No binary compatibility. How important this one is depends on the use cases, but can be acceptable in the case of a new major release
Users have to do some work. If the work is simple enough, then this too may be acceptable.
In the meantime, add new types as enums and keep old types as ints.
The best would be if you could just fix the published versions, if possible. In my opinion consistency would be the best solution, so you would need to do some refactoring. I personally don't like deprecated things, because they get into way. You might be able to wait until a bigger version release and use those ints until then, and refactor everything in a big project. If that is not possible, you might consider yourself stuck with the ints, unless you create some kinds of wrappers or something.
If nothing helps but you still evolve the code, you end up losing consistency or living with the deprecated versions. In any case, usually at least at some point of time people become fed up with old stuff if it has lost it's consistency and create new from scratch... So you would have the refactoring in the future no matter what.
The customer might scrap the project and buy an other product, if something goes wrong. Usually it is not the customer's problem can you afford refactoring or not, they just buy what is appropriate and usable to them. So in the end it is a tricky problem and care needs to be taken.

Categories