Is BitSet worth using? - java

I made a Java object that has a lot of Boolean fields. I was considering using BitSet when I started questioning its usefulness.
Of course, one would use it for memory reasons, since a boolean is 8 bits alone, 4 in an array. With BitSet, each value is stored as a single bit. However, wouldn't the memory saved be blown out of the water by the following overhead?
BitSet class and method definitions meta data (per runtime)
The objects needed as keys to semantically retrieve the values (per class using BitSet)
The meta data for the bits array in BitSet (per instance)
versus using booleans:
boolean value (per instance)
Let's take a look at the following class:
private boolean isVisible; // 8 bits per boolean * 82 booleans = ~0.6Kb
// 81 lines later...
private boolean isTasty;
// ...
public boolean isVisible() { return isVisible; }
// ...
public boolean isTasty() { return isTasty; }
public void setVisible(boolean newVisibility) { isVisible = newVisibility; }
// ...
public void setTasty(boolean newTastiness) { isTasty = newTastiness; }
Now, if I were to combine all my booleans into one BitSet and still keep my code semantic, I might do this:
private static final int _K_IS_VISIBLE = 1; // 32 bits per key * 82 keys = ~2.5Kb
// ...
private static final int _K_IS_TASTY = 82;
private BitSet bools = new BitSet(82); // 2 longs = 64b
// ...
public boolean isVisible() { return bools.get(_K_IS_VISIBLE); }
// ...
public boolean isTasty() { return bools.get(_K_IS_TASTY); }
public void setVisible(boolean newVisibility) { bools.set(_K_IS_VISIBLE, newVisibility); }
// ...
public void setTasty(boolean newTastiness) { bools.set(_K_IS_TASTY, newTastiness); }
tl;dr
costOfUsingBitSet =
bitSetMethodsAndClassMetaData + // BitSet class overhead
(numberOfKeysToRetrieveBits * Integer.SIZE) + // Semantics overhead
(numberOfBitSetsUsed * floor((bitsPerBitSet / Long.SIZE) + 1)); // BitSet internal array overhead
and possibly more. Whereas using booleans would be:
costOfBooleans =
(numberOfBooleansOutsideArrays * 8) +
(numberOfBooleansInsideArrays * 4);
I feel like the overhead of BitSet is much higher. Am I right?

BitSet will be less memory, using only one bit is far more efficient. The method overhead you are looking at is once no matter how many instances of your class you have, so its cost is amortized basically to 0
The advantage of a boolean over an array of booleans or a BitSet is that it is not an Object, so you have one less level of indirection
Cache hits are a primary driver for performance so you have to weigh fewer cache hits with the higher likelihood of evicting data from the cache due to higher memory consumption
Roughly speaking a few booleans will be faster but more memory, as you have more fields or get closer to huge numbers, the scale will top towards BitSet

Nice space comparison here between boolean[] and BitSet:
https://www.baeldung.com/java-boolean-array-bitset-performance
Think they swapped labels here. Should be more bits per memory (Blue) in BitSet.
The key takeaway here is, the BitSet beats the boolean[] in terms of the memory footprint, except for a minimal number of bits.
An alternative in your example is to use 2 long as bit flags.
class A {
// 1st set
private static final long IS_VISIBLE_MASK = 1;
...
private static final long IS_DARK_MASK = 1 << 63 ;
// 2nd set...
private static final long IS_TASTY_MASK = 1;
...
// IS_VISIBLE_MASK .. IS_DARK_MASK
long data1;
// IS_TASTY_MASK ...
long data2;
boolean isDark = (data1 & IS_DARK_MASK) != 0;
}
Limitations
BitSet comes with silly limitations, as you can reach a max of Integer.MAX_VALUE bits. I needed as much bits as I could store in RAM. So modified the original implementation in two ways:
It waste less computing for fixed sized LongBitSets (i.e. user specifies length at construction time).
it can reach the last bit in the biggest possible word array.
Added details on limitations in this thread

Related

How do I return a limited number of cached instances in Java?

I have a "configuration" class that becomes a field of several other classes. It indicates some kind of configuration or "abilities" of those other classes to allow or disallow actions. The configuration class as of now contains a set of four independent booleans and will likely remain like that --or grow with another bolean--. The configuration is immutable: once the object is created, the configuration will never change.
public class Configuration {
private final boolean abilityOne;
private final boolean abilityTwo;
private final boolean abilityThree;
private final boolean abilityFour;
public Configuration (final boolean abilityOne, final boolean abilityTwo,
final boolean abilityThree, final boolean abilityFour) {
this.configuration = ((1 * (abilityOne ? 1 : 0)) +
(2 * (abilityTwo ? 1 : 0)) +
(4 * (abilityThree ? 1 : 0)) +
(8 * (abilityFour ? 1 : 0)));
}
public boolean isAbilityOne() {
return((1 & this.configuration) > 0);
}
public boolean isAbilityTwo() {
return((2 & this.configuration) > 0);
}
public boolean isAbilityThree() {
return((4 & this.configuration) > 0);
}
public boolean isAbilityFour() {
return((8 & this.configuration) > 0);
}
}
Because of C / limited-hardware background, my next implementation (attempt at reducing memory footprint) was with an int used as a bit map: 1 -> first boolean, 2-> second, 4 -> third, 8-> fourth. This way I store an integer and the boolean functions I needed were like:
It works fine and it is quite memory efficient. But it is frowned upon by my Java-all-my-life colleagues.
The number of different configurations is limited (the combinations of boolean values), but the number of objects using them is very large. In order to decrease memory consumption I thought of some kind of "multi-singleton", enumeration or cached instances. And this is where I am now. What is best?
I think multiton pattern is the most efficient way to do this:
public class Configuration {
private static Map<Long, Configuration> configurations = new HashMap<>();
private long key;
private long value;
public static Configuration getInstanse(long key, boolean... configs) {
if (configurations.containsKey(key)) {
return configurations.get(key).setConfigs(configs);
}
Configuration configuration = new Configuration(key, configs);
configurations.put(key, configuration);
return configuration;
}
// Max number of configs.length is 64
private Configuration(long key, boolean... configs) {
this.key = key;
setConfigs(configs);
}
private Configuration setConfigs(boolean[] configs) {
this.value = 0L;
boolean config;
for (int i = 0; i < configs.length; i++) {
config = configs[i];
this.value = this.value | (config ? (1L << i) : 0L);
}
}
public long getKey() {
return key;
}
public boolean getConfig(int place) {
return (value & (1L << place)) == (1L << place);
}
}
I would suggest the following, it is very easy to expand as you just have to add another Ability to your enum.
enum Ability {
Ability1, Ability2, Ability3, Ability4
}
public class Configuration {
private static LoadingCache<Set<Ability>, Configuration> cache = CacheBuilder.newBuilder()
.build(new CacheLoader<Set<Ability>, Configuration>() {
#Override
public Configuration load(Set<Ability> withAbilities) {
return new Configuration(withAbilities);
}
});
Set<Ability> abilities;
private Configuration(Collection<Ability> withAbilities) {
this.abilities = createAbilitySet(withAbilities);
}
public static Configuration create(Ability... withAbilities) {
Set<Ability> searchedAbilities = createAbilitySet(Arrays.asList(withAbilities));
try {
return cache.get(searchedAbilities);
} catch (ExecutionException e) {
Throwables.propagateIfPossible(e);
throw new IllegalStateException();
}
}
private static Set<Ability> createAbilitySet(Collection<Ability> fromAbilities) {
if (fromAbilities.size() == 0) {
return Collections.emptySet();
} else {
return EnumSet.copyOf(fromAbilities);
}
}
public boolean hasAbility(Ability ability) {
return abilities.contains(ability);
}
}
If the configuration implementation objects are small and not expensive to create, there is no need to cache them. Because each monster object will have to keep a reference to each of its configurations, and at machine level a reference is a pointer and uses at least the same memory as an int.
The EnumSet way proposed by #gamulf can probably be used as it without any caching, because according to EnumSet javadoc:
Enum sets are represented internally as bit vectors. This representation is extremely compact and efficient. The space and time performance of this class should be good enough to allow its use as a high-quality, typesafe alternative to traditional int-based "bit flags."
I did not benchmarked it, but caching is likely to be useless with #gamulf's solution because a Configuration object contains only an EnumSet that contains no more than an int.
If you had a heavy configuration class (in term of memory or expensive to create) and only a small number of possible configurations, you could use a static HashSet member in the class, and a static factory method that would return the cached object:
public class Configuration {
static Set<Configuration > confs = new HashSet<>();
...
public Configuration (Ability ... abs) {
...
}
public boolean hasAbility(Ability ab) {
...
}
static Configuration getConfiguration(Ability ... abs) {
for (ConfImpl2 conf: confs) {
if (conf.isSame(abs)) { return conf; }
}
ConfImpl2 conf = new ConfImpl2(abs);
confs.add(conf);
return conf;
}
private boolean isSame(Ability ... abs) {
// ensures that this configuration has all the required abilities and only them
...
}
}
But as I have already said, that is likely to be useless for objects as lightweight as those proposed by #gamulf
I want to share the investigation I made based on your answers, so I'm posting one answer with those results. This way it might be clearer why I choose one answer over other.
The bare result rank are as follows (memory used for 600 "monster" objects, 10% of what will be needed):
trivial option: Class with four booleans inside: 22.200.040
Initial option: Class with one integer as map of bits: 22.200.040
"multiton" option: one factory class that returns references to the trivial option's Class: 4.440.040
EnumSet (without guava cache): 53.401.896 (in this one I probably messed up, since results are not as expected... I might work further on this later on)
EnumSet with guava cache: 4.440.040
Since my tests run first a series of comparisons to ensure that all implementations give the exact same results for all configurations, it has become clear that the 4.440.040 number is the size of the List<> I used to hold the items, for before I resolved to set it to null before measuring memory, those numbers were consistently 0.
Please don't go into how I measured memory consumption (gc(); freeMemory(); before and after I freed each list and set it to null), since I used the same method for all, and performed 20 executions each time and in different orders of execution. Results were consistent enough for me.
These results point at the multiton solution as the easiest of the best performing. That's why I set it as the selected answer.
As side note/curiosity, please be informed that the project for which this investigation started has selected the trivial option as the solution and most of this investigation was made to satisfy my own curiosity --and with some hidden desire to be able to demonstrate that some other solution would be soooo much more efficient than the trivial one... but no--. This is why it took me so long to come up with a conclusion.

what happen if I don't override sizeof when using LruCache class

I read some of the sample using LruCache to implement a cache mechanism for storing the bitmap image. But I still don't know how to use it even through I have read the document http://developer.android.com/reference/android/util/LruCache.html for it.
For example, in document, it mentioned "Returns the size of the entry for key and value in user-defined units." in sizeof(). What is the size of entry mean? is it mean the number of entries it allow, e.g return 10 would allow me to have 10 cache object references.
public class LruBitmapCache extends LruCache<String, Bitmap> implements
ImageCache {
public static int getDefaultLruCacheSize() {
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
return cacheSize;
}
public LruBitmapCache() {
this(getDefaultLruCacheSize());
}
public LruBitmapCache(int sizeInKiloBytes) {
super(sizeInKiloBytes);
}
#Override
protected int sizeOf(String key, Bitmap value) {
return getByteCount / 1024;
...
In the above code, why it need to divide 1024, what is the propose for it?
Also, the constructor LruBitmapCache(int sizeInKiloBytes), why the parameter claim it is size in kilobytes? isn't it should be size in bytes according to the document above?
Any help would be appreciated, thanks! I am confusing...
LruCache is used to cache a limited number of values.
But what is this limited number of values?
First option: you want to store x elements in the cache, whatever their size in memory.
In this case, you just create a LruCache with x as the size and you do not override the sizeOf method.
For instance:
// cache 1000 values, independently of the String size
LruCache<Integer, String> idToCustomerName = new LruCache<>(1000);
Second option, you want to store elements so that the sum of the size of all elements do not exceed a given amount.
In this case, you create a LruCache with y as the overall size and you do override the sizeOf that specify the size of one element in the cache.
For instance:
// cache an undefined number of ids so that the length of all the strings
// do not exceed 100000 characters
LruCache<Integer, String> idToCustomerName = new LruCache<>(100000) {
#Override
protected int sizeOf(Integer key, String value) {
return value.length();
}
};
To answer your questions about the code, the unit used in the cache does not really matter as long as the maxSize variable and the sizeOf are the same unit.
In your example, the internal unit of the cache is kilobytes, so that's why you see /1024 and /8 in the code, which matches the getByteCount / 1024; in the sizeOf method.

Cleaner way to check if a string is ISO country of ISO language in Java

Suppose to have a two-characters String, which should represent the ISO 639 country or language name.
You know, Locale class has two functions getISOLanguages and getISOCountries that return an array of String with all the ISO languages and ISO countries, respectively.
To check if a specific String object is a valid ISO language or ISO country I should look inside that arrays for a matching String. Ok, I can do that by using a binary search (e.g. Arrays.binarySearch or the ApacheCommons ArrayUtils.contains).
The question is: exists any utility (e.g. from Guava or Apache Commons libraries) that provides a cleaner way, e.g. a function that returns a boolean to validate a String as a valid ISO 639 language or ISO 639 Country?
For instance:
public static boolean isValidISOLanguage(String s)
public static boolean isValidISOCountry(String s)
I wouldn't bother using either a binary search or any third party libraries - HashSet is fine for this:
public final class IsoUtil {
private static final Set<String> ISO_LANGUAGES = Set.of(Locale.getISOLanguages());
private static final Set<String> ISO_COUNTRIES = Set.of(Locale.getISOCountries());
private IsoUtil() {}
public static boolean isValidISOLanguage(String s) {
return ISO_LANGUAGES.contains(s);
}
public static boolean isValidISOCountry(String s) {
return ISO_COUNTRIES.contains(s);
}
}
You could check for the string length first, but I'm not sure I'd bother - at least not unless you want to protect yourself against performance attacks where you're given enormous strings which would take a long time to hash.
EDIT: If you do want to use a 3rd party library, ICU4J is the most likely contender - but that may well have a more up-to-date list than the ones supported by Locale, so you would want to move to use ICU4J everywhere, probably.
As far I know there is no any such method in any library but at least you can declare it yourself like:
import static java.util.Arrays.binarySearch;
import java.util.Locale;
/**
* Validator of country code.
* Uses binary search over array of sorted country codes.
* Country code has two ASCII letters so we need at least two bytes to represent the code.
* Two bytes are represented in Java by short type. This is useful for us because we can use Arrays.binarySearch(short[] a, short needle)
* Each country code is converted to short via countryCodeNeedle() function.
*
* Average speed of the method is 246.058 ops/ms which is twice slower than lookup over HashSet (523.678 ops/ms).
* Complexity is O(log(N)) instead of O(1) for HashSet.
* But it consumes only 520 bytes of RAM to keep the list of country codes instead of 22064 (> 21 Kb) to hold HashSet of country codes.
*/
public class CountryValidator {
/** Sorted array of country codes converted to short */
private static final short[] COUNTRIES_SHORT = initShortArray(Locale.getISOCountries());
public static boolean isValidCountryCode(String countryCode) {
if (countryCode == null || countryCode.length() != 2 || countryCodeIsNotAlphaUppercase(countryCode)) {
return false;
}
short needle = countryCodeNeedle(countryCode);
return binarySearch(COUNTRIES_SHORT, needle) >= 0;
}
private static boolean countryCodeIsNotAlphaUppercase(String countryCode) {
char c1 = countryCode.charAt(0);
if (c1 < 'A' || c1 > 'Z') {
return true;
}
char c2 = countryCode.charAt(1);
return c2 < 'A' || c2 > 'Z';
}
/**
* Country code has two ASCII letters so we need at least two bytes to represent the code.
* Two bytes are represented in Java by short type. So we should convert two bytes of country code to short.
* We can use something like:
* short val = (short)((hi << 8) | lo);
* But in fact very similar logic is done inside of String.hashCode() function.
* And what is even more important is that each string object already has cached hash code.
* So for us the conversion of two letter country code to short can be immediately.
* We can relay on String's hash code because it's specified in JLS
**/
private static short countryCodeNeedle(String countryCode) {
return (short) countryCode.hashCode();
}
private static short[] initShortArray(String[] isoCountries) {
short[] countriesShortArray = new short[isoCountries.length];
for (int i = 0; i < isoCountries.length; i++) {
String isoCountry = isoCountries[i];
countriesShortArray[i] = countryCodeNeedle(isoCountry);
}
return countriesShortArray;
}
}
The Locale.getISOCountries() will always create a new array so we should store it into a static field to avoid non necessary allocations.
In the same time HashSet or TreeSet consumes a lot of memory so this validator will use a binary search on array. This is a trade off between speed and memory.

Are there any tricks to reduce memory usage when storing String data type in hashmap?

I need to store value pair (word and number) in the Map.
I am trying to use TObjectIntHashMap from Trove library with char[] as the key, because I need to minimize the memory usage. But with this method, I can not get the value when I use get() method.
I guess I can not use primitive char array to store in a Map because hashcode issues.
I tried to use TCharArrayList but that takes much memory also.
I read in another stackoverflow question that similar with my purpose and have suggestion to use TLongIntHashMap , store encode values of String word in long data type. In this case my words may contains of latin characters or various other characters that appears in wikipedia collections, I do not know whether the Long is enough for encode or not.
I have tried using Trie data structure to store it, but I need to consider my performance also and choose the best for both memory usage and performance.
Do you have any idea or suggestion for this issue?
It sounds like the most compact way to store the data is to use a byte[] encoded in UTF-8 or similar. You can wrap this in your own class or write you own HashMap which allows byte[] as a key.
I would reconsider how much time it is worth spending to save some memory. If you are talking about a PC or Server, at minimum wage you need to save 1 GB for an hours work so if you are only looking to save 100 MB that's about 6 minutes including testing.
Write your own class that implements CharSequence, and write your own implementation of equals() and hashcode(). The implementation would also pre-allocate large shared char[] storage, and use bits of it at a time. (You can definitely incorporate #Peter Lawrey's excellent suggestion into this, too, and use byte[] storage.)
There's also an opportunity to do a 'soft intern()' using an LRU cache. I've noted where the cache would go.
Here's a simple demonstration of what I mean. Note that if you need heavily concurrent writes, you can try to improve the locking scheme below...
public final class CompactString implements CharSequence {
private final char[] _data;
private final int _offset;
private final int _length;
private final int _hashCode;
private static final Object _lock = new Object();
private static char[] _storage;
private static int _nextIndex;
private static final int LENGTH_THRESHOLD = 128;
private CompactString(char[] data, int offset, int length, int hashCode) {
_data = data; _offset = offset; _length = length; _hashCode = hashCode;
}
private static final CompactString EMPTY = new CompactString(new char[0], 0, 0, "".hashCode());
private static allocateStorage() {
synchronized (_lock) {
_storage = new char[1024];
_nextIndex = 0;
}
}
private static CompactString storeInShared(String value) {
synchronized (_lock) {
if (_nextIndex + value.length() > _storage.length) {
allocateStorage();
}
int start = _nextIndex;
// You would need to change this loop and length to do UTF encoding.
for (int i = 0; i < value.length(); ++i) {
_storage[_nextIndex++] = value.charAt(i);
}
return new CompactString(_storage, start, value.length(), value.hashCode());
}
}
static {
allocateStorage();
}
public static CompactString valueOf(String value) {
// You can implement a soft .intern-like solution here.
if (value == null) {
return null;
} else if (value.length() == 0) {
return EMPTY;
} else if (value.length() > LENGTH_THRESHOLD) {
// You would need to change .toCharArray() and length to do UTF encoding.
return new CompactString(value.toCharArray(), 0, value.length(), value.hashCode());
} else {
return storeInShared(value);
}
}
// left to reader: implement equals etc.
}

Can I allocate objects contiguously in java?

Assume I have a large array of relatively small objects, which I need to iterate frequently.
I would like to optimize my iteration by improving cache performance, so I would like to allocate the objects [and not the reference] contiguously on the memory, so I'll get fewer cache misses, and the overall performance could be segnificantly better.
In C++, I could just allocate an array of the objects, and it will allocate them as I wanted, but in java - when allocating an array, I only allocate the reference, and the allocation is being done one object at a time.
I am aware that if I allocate the objects "at once" [one after the other], the jvm is most likely to allocate the objects as contiguous as it can, but it might be not enough if the memory is fragmented.
My questions:
Is there a way to tell the jvm to defrag the memory just before I start allocating my objects? Will it be enough to ensure [as much as possible] that the objects will be allocated continiously?
Is there a different solution to this issue?
New objects are creating in the Eden space. The eden space is never fragmented. It is always empty after a GC.
The problem you have is when a GC is performed, object can be arranged randomly in memory or even surprisingly in the reverse order they are referenced.
A work around is to store the fields as a series of arrays. I call this a column-based table instead of a row based table.
e.g. Instead of writing
class PointCount {
double x, y;
int count;
}
PointCount[] pc = new lots of small objects.
use columns based data types.
class PointCounts {
double[] xs, ys;
int[] counts;
}
or
class PointCounts {
TDoubleArrayList xs, ys;
TIntArrayList counts;
}
The arrays themselves could be in up to three different places, but the data is otherwise always continuous. This can even be marginally more efficient if you perform operations on a subset of fields.
public int totalCount() {
int sum = 0;
// counts are continuous without anything between the values.
for(int i: counts) sum += i;
return i;
}
A solution I use is to avoid GC overhead for having large amounts of data is to use an interface to access a direct or memory mapped ByteBuffer
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class MyCounters {
public static void main(String... args) {
Runtime rt = Runtime.getRuntime();
long used1 = rt.totalMemory() - rt.freeMemory();
long start = System.nanoTime();
int length = 100 * 1000 * 1000;
PointCount pc = new PointCountImpl(length);
for (int i = 0; i < length; i++) {
pc.index(i);
pc.setX(i);
pc.setY(-i);
pc.setCount(1);
}
for (int i = 0; i < length; i++) {
pc.index(i);
if (pc.getX() != i) throw new AssertionError();
if (pc.getY() != -i) throw new AssertionError();
if (pc.getCount() != 1) throw new AssertionError();
}
long time = System.nanoTime() - start;
long used2 = rt.totalMemory() - rt.freeMemory();
System.out.printf("Creating an array of %,d used %,d bytes of heap and tool %.1f seconds to set and get%n",
length, (used2 - used1), time / 1e9);
}
}
interface PointCount {
// set the index of the element referred to.
public void index(int index);
public double getX();
public void setX(double x);
public double getY();
public void setY(double y);
public int getCount();
public void setCount(int count);
public void incrementCount();
}
class PointCountImpl implements PointCount {
static final int X_OFFSET = 0;
static final int Y_OFFSET = X_OFFSET + 8;
static final int COUNT_OFFSET = Y_OFFSET + 8;
static final int LENGTH = COUNT_OFFSET + 4;
final ByteBuffer buffer;
int start = 0;
PointCountImpl(int count) {
this(ByteBuffer.allocateDirect(count * LENGTH).order(ByteOrder.nativeOrder()));
}
PointCountImpl(ByteBuffer buffer) {
this.buffer = buffer;
}
#Override
public void index(int index) {
start = index * LENGTH;
}
#Override
public double getX() {
return buffer.getDouble(start + X_OFFSET);
}
#Override
public void setX(double x) {
buffer.putDouble(start + X_OFFSET, x);
}
#Override
public double getY() {
return buffer.getDouble(start + Y_OFFSET);
}
#Override
public void setY(double y) {
buffer.putDouble(start + Y_OFFSET, y);
}
#Override
public int getCount() {
return buffer.getInt(start + COUNT_OFFSET);
}
#Override
public void setCount(int count) {
buffer.putInt(start + COUNT_OFFSET, count);
}
#Override
public void incrementCount() {
setCount(getCount() + 1);
}
}
run with the -XX:-UseTLAB option (to get accurate memory allocation sizes) prints
Creating an array of 100,000,000 used 12,512 bytes of heap and took 1.8 seconds to set and get
As its off heap, it has next to no GC impact.
Sadly, there is no way of ensuring objects are created/stay at adjacent memory locations in Java.
However, objects created in sequence will most likely end up adjacent to each other (of course this depends on the actual VM implementation). I'm pretty sure that the writers of the VM are aware that locality is highly desirable and don't go out of their way to scatter objects randomly around.
The Garbage Collector will at some point probably move the objects - if your objects are short lived, that should not be an issue. For long lived objects it then depends on how the GC implements moving the survivor objects. Again, I think its reasonable that the guys writing the GC have spent some thought on the matter and will perform copies in a way that does not screw locality more than unavoidable.
There are obviously no guarantees for any of above assumptions, but since we can't do anything about it anyway, stop worring :)
The only thing you can do at the java source level is to sometimes avoid composition of objects - instead you can "inline" the state you would normally put in a composite object:
class MyThing {
int myVar;
// ... more members
// composite object
Rectangle bounds;
}
instead:
class MyThing {
int myVar;
// ... more members
// "inlined" rectangle
int x, y, width, height;
}
Of course this makes the code less readable and duplicates potentially a lot of code.
Ordering class members by access pattern seems to have a slight effect (I noticed a slight alteration in a benchmarked piece of code after I had reordered some declarations), but I've never bothered to verify if its true. But it would make sense if the VM does no reordering of members.
On the same topic it would also be nice to (from a performance view) be able to reinterpret an existing primitive array as another type (e.g. cast int[] to float[]). And while you're at it, why not whish for union members as well? I sure do.
But we'd have to give up a lot of platform and architecture independency in exchange for these possibilities.
Doesn't work that way in Java. Iteration is not a matter of increasing a pointer. There is no performance impact based on where on the heap the objects are physically stored.
If you still want to approach this in a C/C++ way, think of a Java array as an array of pointers to structs. When you loop over the array, it doesn't matter where the actual structs are allocated, you are looping over an array of pointers.
I would abandon this line of reasoning. It's not how Java works and it's also sub-optimization.

Categories