Contextualisation
Im am implementing a bytecode instrumenter using the soot framework in a testing context and I want to know which design is better.
I am building the TraceMethod object for every Method in a Class that I am instrumenting and I want to run this instrumenter on multiple Classes.
Which Option offers more performance(Space–time)?
Option 1: (Maps)
public class TraceMethod {
boolean[] decisionNodeList;
boolean[] targetList;
Map<Integer,List<Integer>> dependenciesMap;
Map<Integer,List<Double>> decisionNodeBranchDistance;
}
Option 2: (Objects)
public class TraceMethod {
ArrayList<Target> targets = new ArrayList<Target>();
ArrayList<DecisionNode> decisionNodes = new ArrayList<DecisionNode>();
}
public class DecisionNode {
int id;
Double branchDistance;
boolean reached;
}
public class Target {
int id;
boolean reached;
List<DecisionNode> dependencies;
}
I have implemented the option 2 by myself, but my boss suggest me the option 1 and he argue that is "lighter". I saw that in this article "Class Object vs Hashmap" that HashMaps use more memory than Objects, but im still not convinced that my solution(option 2) is better.
Its a simple detail but i want to be sure that I am using the optimal solution, my concern is about performance(Space–time). I know that the second option are way better in term of maintainability but i can sacrifice that if its not optimal.
Approach 1 has the potentical to be much faster and uses less space.
Especially for a byte code instrumenter, I would first implement approach 1.
And then when it works, replace both Lists with non generic lists that use primitive types instead of the Integer and Double object.
Note that an int needs 4 bytes while an Integer (Object) need 16 - 20 bytes, depending on the machine (16 at PC, 20 at android).
The List can be replaced with GrowingIntArray
(I have found that in an statistic package of Apache if I remeber correctly) which uses primitive ints. (Or maybe just replaced by an int[] once you know that the content cannot change anymore)
Then you just write your own GrowingDoubleArray (or use double[])
Remember Collections are handy but slower.
Objects use 4 times more space than primitives.
A byte code instrumenter needs performance, it is not a software that is run once a week.
Finally I would not replace that Maps with non generic ones, that seems for me to much work. But you may try it as last step.
As a final optimization step: look how many elements are in your lists or maps. If that are usually less than 16 (you have to try that out), you may switch to a linear search, which is the fastest, for a very low number of elements. You even can make your code intelligent to switch the search algorithms once the number of elements exceed a specific number. (Sun/Oracle java does this, and Apple/ios, to) in some of their Collections. However this last step will make you code much more complex.
Space as an exmample:
DecisionNode: 16 for the class + 4 (id) + 20 (Double) +4 (boolean) = 44 + 4 padding to then next multiple of 8 = 48 bytes.
In general you should always go for maintenance, and not for supposed performance. There are few good reasons for this:
- We tend to be fascinated by speed difference between array vs HashMap, but in real enterprise application these differences are not big enough to account in visible difference in application speed.
- Most common bottlenecks in application are in either database or network.
- JVM optimizes code to some extent
It is very unlikely that your application will have performance issues due to maintainable code. More likely case is your boss will run out of money when you will have millions lines of unmaintainable code .
来源:https://stackoverflow.com/questions/31816774/code-design-performance-vs-maintainability