Performance of extra string comparisons vs HashMap lookups - java

Assume I am running either of the code snippets below for a list of 1000 Event entries (in allEventsToAggregate). Would I see a performance improvement in the first implementation if the events in allEventsToAggregate are sorted by customerId, with each customer having roughly 3 events? This is essentially a question of string comparison vs. HashMap lookup performance.
Option 1:
Map<String, List<Event>> eventsByCust = new HashMap<String, List<Event>>();
List<Event> thisCustEntries;
String lastCust = null;
for (Event thisEvent : allEventsToAggregate) {
if (!thisEvent.getCustomerId().equals(lastCust)) {
thisCustEntries = eventsByCust.get(thisEvent.getCustomerId());
if (thisCustEntries == null) {
thisCustEntries = new ArrayList<Event>();
}
}
thisCustEntries.add(thisEvent);
eventsByCust.put(thisEvent.getCustomerId(), thisCustEntries);
lastCust = thisEvent.getCustomerId();
}
Option 2:
Map<String, List<Event>> eventsByCust = new HashMap<String, List<Event>>();
for (Event thisEvent : allEventsToAggregate) {
List<Event> thisCustEntries = eventsByCust.get(thisEvent.getCustomerId());
if (thisCustEntries == null) {
thisCustEntries = new ArrayList<Event>();
}
thisCustEntries.add(thisEvent);
}

Would I see a performance improvement
Almost certainly not. Unless this block represents a critical inner loop of your application, any marginal performance gains will almost certainly be unnoticeable.
Consequently, I would go with the second version of the code, as its a clearer expression of your intent and so will be easier to maintain (as well as being slightly less prone to subtle bugs in the first place). Maintainability almost certainly trumps making the application 0.001% faster.

1) Remember that a successful retrieval of an item from a HashMap requires a string compare to confirm that you really have found the correct item.
2) We seem to be talking about very small differences in execution time, not real algorithmic improvements. Is it really worth losing readability for this?
3) For small differences, the only way to really know will be to actually time the thing in practice - in fact not only to run a comparison, but to organise it as a fully fledged scientific experiment. There is just too much too to worry about these days about what your compiler and run time system has chosen to optimise, what cpu caching or VM page faulting means, and what Java garbage collection thinks of your algorithm. Then, of course, you may well find that you get different answers for different versions of Java or on hardware with different cpus, motherboards, or memory sizes, or even how long the system has been running and so how much time it has had to migrate its disk contents into memory cache, or JIT-compile relevant bits of Java, or whatever.

Related

Java lambda expressions and memory allocation

Let's say we have three methods like these. They all do the same, but how different they are in terms of memory allocation and efficiency? Method one will create instances of Function during each call, but will the second method do the same? Should one always use the third version or either version is safe and the JIT compiler will take care of the memory optimization?
class Test {
Map<String, Set<String>> mapOfSets = new HashMap<>();
static final Function<String, Set<String>> FUNCTION = s -> new HashSet<>();
void method1(String key, String value) {
Function<String, Set<String>> func = s -> new HashSet<>();
mapOfSets.computeIfAbsent(key, func).add(value);
}
void method2(String key, String value) {
mapOfSets.computeIfAbsent(key, s -> new HashSet<>()).add(value);
}
void method3(String key, String value) {
mapOfSets.computeIfAbsent(key, FUNCTION).add(value);
}
}
There are two ways to answer this:
The Java Language Specification section that describes the evaluation of a lambda expression is written to give compilers / implementations flexibility in whether or not a new object is created. Your question boils down to whether or not the lambda objects are reused ... in the three versions of the code. According to the JLS, the answer is implementation dependent. The only way to be sure is to dump out the native code produced by the JIT compiler1.
Given that the behavior and therefore the performance will depend on the Java implementation, it is debatable whether one should pick one form over the other:
On the one hand, the third form may be faster.
On the other hand, the second and (maybe) first forms are more readable.
Should one write applications to be a bit faster at the expense of readability? (Q1)
Should one do that, in the face of an expectation that the next version of the compiler may make the hand optimization redundant? (Q2)
This is a matter of opinion, but my opinion is that the answers to Q1 & Q2 is "No" under most circumstances. Leave it to the JIT compiler to deal with optimization unless
profiling tells you that the optimization is significant to the application's overall performance,
the application's overall performance really matters.
1 - See How to see JIT-compiled code in JVM?.
As comments have mentioned, this can change depending on what Java implementation you use.
For the hotspot jvm (the normal/default java implementation) you can expect all 3 options to be identical in terms of performance. You may think at first that 3 might be better since 1 and 2 might need to create a new Function object each time they are called. However, your assumption that a new object needs to be created each time for method1 (and 2) is false since a non-capturing lambda will be cached at the call site, so the same instance will be reused for each call just like with method3 and you would not see any difference.
this answer is and excellent overview of the topic goes into more detail
So you can use any of the three without worry in this case so use whichever is the most clear for your situation.

Combining multiple java streams in a structured way

I want to use Java's stream API to do some calculations on a list of objects:
List<Item>.stream()...
The Item class contains many attributes. For some of those I need to take the average value across all items in the collection, for other attributes I need to do other forms of calculations. I have been doing separate stream/collector calls to achieve this and although I'm not running into any performance issues (because the list size is usually about 100) I want to learn how to be more concise, aka loop once.
ItemCalculation itemCalculation = ItemCalculation.builder()
.amountOfItems(itemList.size())
.averagePrice(itemList.stream()
.mapToDouble(item -> item.getPrice())
.average()
.getAsDouble())
.averageInvestmentValue(itemList.stream()
.mapToDouble(item -> getTotalInvestmentValue(item.getInvestmentValue(), item.getInvestmentValuePackaging()))
.average()
.getAsDouble())
.highestWarrantyLimit(itemList.stream()... etc.
I read about creating a custom collector, but it seems a bit weird to have my "calculation" class be just one line (stream->customCollector) and then have a very bloated collector class that does the actual logic. Especially because different attributes are collected in a different way I would need many different intermediate count and other variables. Any thoughts?
Unfortunately, it doesn't seem possible to reasonably improve it using streams so it can perform better in a single-thread mode.
The code you provided in your question is clear for understanding and sufficiently performant for small collection as it is now.
If you'd like to boost the performance of your solution, you can iterate over your collection just once in an iterative manner, calculating every output you need in a single run:
long amountOfItems = 0;
double priseSum = 0;
double highestWarrantyLimit = Double.MIN_VALUE;
for (Item item : itemList) {
amountOfItems++;
priseSum += item.getPrice();
double investmentValue = getTotalInvestmentValue(item.getInvestmentValue(), item.getInvestmentValuePackaging());
if (highestWarrantyLimit < investmentValue) {
highestWarrantyLimit = investmentValue;
}
}
ItemCalculation itemCalculation = ItemCalculation.builder()
.amountOfItems(amountOfItems)
.averagePrice(priseSum / amountOfItems)
.averageInvestmentValue(investmentValueSum / amountOfItems)
.highestWarrantyLimit(highestWarrantyLimit)
// ...
.build();
The streams API was added to provide library support for processing sequences of data elements, which is very true for your case. However, streams impose a common pipeline for data elements, which is not true for your case and makes the pipeline to look like:
itemList.stream()
.collect(toItemCalculation());
Which is not very reasonable, unless you're going to your it in the multi-threaded mode. In such case, a solution that utilizes a custom collector would be preferable since the scaffolding-code for combining logic is already built-in.

Is in If condition the check obj == null more performance effective then obj!=null

According to one of my senior coding style 2 is better in performance than coding style 1,is it true ?
coding style 1
If (obj!=null)
{
Logic A
}
else
{
Logic B
}
coding style 2
If (obj==null)
{
Logic B
}
else
{
Logic A
}
In examples like this, it is developer performance you should worry about. What is the cleanest and simplest code and this will often be a pattern the JIT optimiser will do a good job, if not it's best job with.
In this example, even if you could measure a difference I would suspect
your test is broken.
the difference would be different on different machines or JVMs.
such a difference will disappear in future versions of Java.
BTW: Looking at the byte code is a very poor measure of performance, but I can say that the byte code generates and I suspect the native code generated will be the same length.
Whether or not it is faster to test for null or non-null can only be observed at the machine code level, which you have almost no control over. Your Java file will be compiled to bytecode and then, at runtime, this bytecode will be compiled again to machine code by the Just-in-Time compiler (JIT). The JIT performs all kids of advanced optimizations, one of which could be the reordering of branches with inversion of branching conditions.
Note that the CPU cycles spent on testing the value of the variable will be dwarfed by the penalty in branch misprediction, so correct branch prediction is the JIT compiler's foremest concern. You have absolutely zero control of these subtleties on the level of Java source code.
Therefore the unanimous advice is to take care to write code which reads most naturally and don't spend a second thinking on the peformance differential of branch ordering.
Performance should not make a difference as Peter Lawrey pointed out.
I guess your friend didn't talk about performance but about coding style.
When using if elseconstructs people often find it preferable to first handle the non-negated case in the if block before handling the negated case in the else block.
So one better writes
if (a == b) {
logicA();
}
else {
logicB();
}
instead of writing
if (a != b) {
logicB();
}
else {
logicA();
}
as it usually leads to better readability.
Your senior should provide "measurable" test cases to prove to you which logic is better.
You shouldn't worry about optimization logic as the compiler does all the optimization for you. Rather, focus on readability of code (and flow logic?). Example, Java 7 introduced <T> T Objects.requireNotNull(T obj) method, which internally does:
if (obj == null) {
throw new NullPointerException();
}
return x;
Which is better, especially when creating API's as you have a business logic that "asserts"/guarantees not null.

Can Java compiler optimize adding to a set in recursive methods

Simple question asked mostly out of curiosity about what java compiler's are smart enough to do. I know not all compilers are built equally, but I'm wondering if others feel it's reasonable to expect an optimization on most compilers I'm likely to run against, not if it works on a specific version or on all versions.
So lets say that I have some tree structure and I want to collect all the descendant of a node. There are two easy ways to do this recursively.
The more natural method, for me, to do this would be something like this:
public Set<Node> getDescendants(){
Set<Node> descendants=new HashSet<Node>();
descendants.addall(getChildren());
for(Node child: getChildren()){
descendants.addall(child.getDescendants());
}
return descendants;
}
However, assuming no compiler optimizations and a decent sized tree this could get rather expensive. On each recursive call I create and fully populate a set, only to return that set up the stack so the calling method can add the contents of my returning set to it's version of the descendants set, discarding the version that was just built and populated in the recursive call.
So now I'm creating many sets just to have them be discarded as soon as I return their contents. Not only do I pay a minor initialization cost for building the sets, but I also pay the more substantial cost of moving all the contents of one set into the larger set. In large trees most of my time is spent moving Nodes around in memory from set A to B. I think this even makes my algorithm O(n^2) instead of O(n) due to the time spent copying Nodes; though it may work out to being O(N log(n)) if I set down to do the math.
I could instead have a simple getDescendants method that calls a helper method that looks like this:
public Set<Node> getDescendants(){
Set<node> descendants=new HashSet<Node>();
getDescendantsHelper(descendants);
return descendants;
}
public Set<Node> getDescendantsHelper(Set<Node> descendants){
descendants.addall(getChildren());
for(Node child: getChildren()){
child.getDescendantsHelper(descendant);
}
return nodes;
}
This ensures that I only ever create one set and I don't have to waste time copying from one set to another. However, it requires writing two methods instead of one and generally feels a little more cumbersome.
The question is, do I need to do option two if I'm worried about optimizing this sort of method? or can I reasonably expect the java compiler, or JIT, to recognize that I am only creating temporary sets for convenience of returning to the calling method and avoid the wasteful copying between sets?
edit: cleaned up bad copy paste job which lead to my sample method adding everything twice. You know something is bad when your 'optimized' code is slower then your regular code.
The question is, do I need to do option two if I'm worried about optimizing this sort of method?
Definitely yes. If performance is a concern (and most of the time it is not!), then you need it.
The compiler optimizes a lot but on a very different scale. Basically, it works with one method only and it optimizes the most commonly used path there in. Due to heavy inlining it can sort of optimize across method calls, but nothing like the above.
It can also optimize away needless allocations, but only in very simple cases. Maybe something like
int sum(int... a) {
int result = 0;
for (int x : a) result += x;
return result;
}
Calling sum(1, 2, 3) means allocating int[3] for the varargs arguments and this can be eliminated (if the compiler really does it is a different question). It can even find out that the result is a constant (which I doubt it really does). If the result doesn't get used, it can perform dead code elimination (this happens rather often).
Your example involves allocating a whole HashMap and all its entries, and is several orders of magnitude more complicated. The compiler has no idea how a HashMap works and it can't find out e.g., that after m.addAll(m1) the set m contains all member of m1. No way.
This is an algorithmical optimization rather than low-level. That's what humans are still needed for.
For things the compiler could do (but currently fails to), see e.g. these questions of mine concerning associativity and bounds checks.

Is there such a thing as too many embedded if-statements?

Currently I am working on a bit of code which (I believe) requires quite a few embedded if statements. Is there some standard to how many if statements to embed? Most of my googling has turned up things dealing with excel..don't know why.
If there is a standard, why? Is it for readability or is it to keep code running more smoothly? In my mind, it makes sense that it would be mainly for readability.
An example of my if-structure:
if (!all_fields_are_empty):
if (id_search() && validId()):
// do stuff
else if (name_search):
if (name_exists):
if (match < 1):
// do stuff
else:
// do stuff
else if (name_search_type_2):
if (exists):
if (match < 1):
// do stuff
else:
// do stuff
else:
// you're stupid
I have heard that there's a limit to 2-3 nested for/while loops, but is there some standard for if-statements?
Update:
I have some years under my belt now. Please don't use this many if statements. If you need this many, your design is probably bad. Today, I LOVE when I can find an elegant way to do these things with minimal if statements or switch cases. The code ends up cleaner, easier to test, and easier to maintain. Normally.
As Randy mentioned, the cause of this kind of code is in most cases a poor design of an application. Usually I try to use "processor" classes in your case.
For example, given that there is some generic parameter named "operation" and 30 different operations with different parameters, you could make an interface:
interface OperationProcessor {
boolean validate(Map<String, Object> parameters);
boolean process(Map<String, Object> parameters);
}
Then implement lots of processors for each operation you need, for example:
class PrinterProcessor implements OperationProcessor {
boolean validate(Map<String, Object> parameters) {
return (parameters.get("outputString") != null);
}
boolean process(Map<String, Object> parameters) {
System.out.println(parameters.get("outputString"));
}
}
Next step - you register all your processors in some array when application is initialized:
public void init() {
this.processors = new HashMap<String, OperationProcessor>();
this.processors.put("print",new PrinterProcessor());
this.processors.put("name_search", new NameSearchProcessor());
....
}
So your main method becomes something like this:
String operation = parameters.get("operation"); //For example it could be 'name_search'
OperationProcessor processor = this.processors.get(operation);
if (processor != null && processor.validate()) { //Such operation is registered, and it validated all parameters as appropriate
processor.process();
} else {
System.out.println("You are dumb");
}
Sure, this is just an example, and your project would require a bit different approach, but I guess it could be similiar to what I described.
I don't think there is a limit but i wouldn't recommend embeddeding more the two - it's too hard to read, difficult to debug and hard to unit test. Consider taking a look at a couple great books like Refactoring, Design Patterns, and maybe Clean Code
Technically, I am not aware of any limitation to nesting.
It might be an indicator of poor design if you find yourself going very deep.
Some of what you posted looks like it may be better served as a case statement.
I would be concerned with readability, and code maintenance for the next person which really means it will be difficult - even for the first person (you) - to get it all right in the first place.
edit:
You may also consider having a class that is something like SearchableObject(). You could make a base class of this with common functionality, then inherit for ID, Name, etc, and this top level control block would be drastically simplified.
Technically you can have as many as you like but if you have a lot it can quickly make the code unreadable.
What i'd normally do is something like:
if(all_fields_are_empty) {
abuseuser;
return;
}
if(id_search() && validId()) {
//do stuff
return;
}
if(name_search)
{
if(name_exists)
//do stuff
return
else
//do stuff
return
}
I'm sure you get the picture
Tl;Dr You don't really want anymore than 10-15 paths though any one method
What your essentially referring to here is Cyclomatic complexity.
Cyclomatic complexity is a software metric (measurement), used to
indicate the complexity of a program. It is a quantitative measure of
the number of linearly independent paths through a program's source
code. It was developed by Thomas J. McCabe, Sr. in 1976.
So every if statement is potentially a new path though your code and increases it's Cyclomatic complexity. There are tools that will measure this for you and high light areas of high complexity for potential refactoring.
Is there some standard to how many if statements to embed?
Yes and no. It's generally regarded (and McCabe himself argued) that a Cyclomatic complexity of over about 10 or 15 is too high and a sign that the code should be refactored.
One of McCabe's original applications was to limit the complexity of
routines during program development; he recommended that programmers
should count the complexity of the modules they are developing, and
split them into smaller modules whenever the cyclomatic complexity of
the module exceeded 10.[2] This practice was adopted by the NIST
Structured Testing methodology, with an observation that since
McCabe's original publication, the figure of 10 had received
substantial corroborating evidence, but that in some circumstances it
may be appropriate to relax the restriction and permit modules with a
complexity as high as 15. As the methodology acknowledged that there
were occasional reasons for going beyond the agreed-upon limit, it
phrased its recommendation as: "For each module, either limit
cyclomatic complexity to [the agreed-upon limit] or provide a written
explanation of why the limit was exceeded."[7]
This isn't really a hard rule though and can be disregarded in some circumstances. See this question What is the highest Cyclomatic Complexity of any function you maintain? And how would you go about refactoring it?.
why? Is it for readability or is it to keep code running more
smoothly?
Essentially this is for readability, which should make your code run smoothly. To quote Martin Fowler
Any fool can write code that a computer can understand. Good
programmers write code that humans can understand.
The only technical limit to the number of nested if/else blocks in Java will probably be the size of your stack. Style is another matter.
Btw: What's with the colons?

Categories

Resources