Is using an integer flag and bitwise operations an effective way of reducing the memory footprint of high volume Objects?
-
Memory Footprint
It is my understanding that commonly a
booleanis stored as anintin a JVM implementation. Is this correct? In which case surely the 32 flags represent a large memory footprint reduction.Although of course the JVM implementations vary, so this may not always be the case.
-
Performance
It is my understanding that CPUs are very number driven and bitwise operations are about as efficient as things come in computing.
Is there a performance penalty – or even gain – to using bitwise operations over boolean operations?
-
Alternatives
Is there a better way of accomplishing the same thing? Does an Enum allow the combination of flags i.e.
FLAGX = FLAG1 | FLAG2?
Example Code
Note the last method propogateMove() is recursive and may be called many hundreds of times per second and has a direct effect on the responsiveness of our application, hence the usage of flags to avoid logic bits and calling other methods.
// FLAGS helper functions
private final void setclear(int mask, boolean set) { if (set) set(mask); else clear(mask); }
private final void set(int mask) { flags |= mask; }
private final void clear(int mask) { flags &= ~mask; }
private final boolean test(int mask) { return ((flags & mask) == mask); }
// Flags //////////////////////////////////////////////////////////////////////
private static final boolean HORIZONTAL = true;
private static final boolean VERTICAL = false;
private static final int ORIENT = 0x00000001;
private static final int DISPLAY = 0x00000002;
private static final int HSHRINK = 0x00000004;
private static final int VSHRINK = 0x00000008;
private static final int SHRINK = HSHRINK | VSHRINK;
private static final int TILE_IMAGE = 0x00000010;
private static final int CURSOR = 0x00000020;
private static final int MOUSEINSIDE = 0x00000040;
private static final int MOUSEINSIDE_BLOCKED = 0x00000080;
private static final int CONSTRAIN = 0x00000100;
private static final int CONSTRAIN_DESCENDENT = 0x00000200;
private static final int PLACE = 0x00000400;
private static final int PLACE_DESCENDENT = 0x00000800;
private static final int REFLOW = CONSTRAIN | CONSTRAIN_DESCENDENT | PLACE | PLACE_DESCENDENT;
private static final int PACK = 0x00001000;
private static final int CLIP = 0x00002000;
private static final int HAS_WIDTH_SLACK = 0x00004000;
private static final int HAS_HEIGHT_SLACK = 0x00008000;
private static final int ALIGN_TOP = 0x00010000;
private static final int ALIGN_BOTTOM = 0x00020000;
private static final int ALIGN_LEFT = 0x00040000;
private static final int ALIGN_RIGHT = 0x00080000;
private static final int ALIGNS = ALIGN_TOP | ALIGN_BOTTOM | ALIGN_LEFT | ALIGN_RIGHT;
private static final int ALIGN_TOPLEFT = ALIGN_TOP | ALIGN_LEFT;
private static final int ALIGN_TOPRIGHT = ALIGN_TOP | ALIGN_RIGHT;
private static final int ALIGN_BOTTOMLEFT = ALIGN_BOTTOM | ALIGN_LEFT;
private static final int ALIGN_BOTTOMRIGHT = ALIGN_BOTTOM | ALIGN_RIGHT;
private static final int ENTER_TRAP = 0x00100000;
private static final int LEAVE_TRAP = 0x00200000;
private static final int _MOVE_TRAP = 0x00400000;
private static final int MOVE_TRAP = 0x00800000;
private static final int CHILDREN_READ_TRAP = 0x01000000;
private static final int CHILDREN_TRAP = 0x02000000;
private static final int PLACE_CLEAN = 0x03000000;
private static final int SHRINK_TRAP = 0x04000000;
private static final int HSHRINK_TRAP = 0x10000000;
private static final int VSHRINK_TRAP = 0x20000000;
//private static final int UNUSED = 0x40000000;
//private static final int UNUSED = 0x80000000;
// Flags in switch ////////////////////////////////////////////////////////////
/** get align value as a string from align flags */
private JS alignToJS() {
switch(flags & ALIGNS) {
case (ALIGN_TOPLEFT):
return SC_align_topleft;
case (ALIGN_BOTTOMLEFT):
return SC_align_bottomleft;
case (ALIGN_TOPRIGHT):
return SC_align_topright;
case (ALIGN_BOTTOMRIGHT):
return SC_align_bottomright;
case ALIGN_TOP:
return SC_align_top;
case ALIGN_BOTTOM:
return SC_align_bottom;
case ALIGN_LEFT:
return SC_align_left;
case ALIGN_RIGHT:
return SC_align_right;
case 0: // CENTER
return SC_align_center;
default:
throw new Error("This should never happen; invalid alignment flags: " + (flags & ALIGNS));
}
}
// Flags in logic /////////////////////////////////////////////////////////////
private final boolean propagateMove(int mousex, int mousey) throws JSExn {
// start with pre-event _Move which preceeds Enter/Leave
if (test(_MOVE_TRAP)) {
if (Interpreter.CASCADE_PREVENTED == justTriggerTraps(SC__Move, JSU.T)) {
// _Move cascade prevention induces Leave
propagateLeave();
// propagate cascade prevention
return true;
}
}
// REMARK: anything from here on in is a partial interruption relative
// to this box so we can not call propagateLeave() directly upon it
int i;
boolean interrupted = false;
if (!test(PACK)) {
// absolute layout - allows for interruption by overlaying siblings
for (Box b = getChild(i=treeSize()-1); b != null; b = getChild(--i)) {
if (!b.test(DISPLAY)) {
continue;
}
if (interrupted) {
b.propagateLeave();
continue;
}
int b_mx = mousex-getXInParent(b);
int b_my = mousey-getYInParent(b);
if (b.inside(b_mx, b_my)) {
if (b.propagateMove(b_mx, b_my)) {
interrupted = true;
}
} else {
b.propagateLeave();
}
}
} else {
// packed layout - interrupted still applies, plus packedhit shortcut
boolean packedhit = false;
for (Box b = getChild(i=treeSize()-1); b != null; b = getChild(--i)) {
if (!b.test(DISPLAY)) {
continue;
}
if (packedhit) {
b.propagateLeave();
continue;
}
int b_mx = mousex-getXInParent(b);
int b_my = mousey-getYInParent(b);
if (b.inside(b_mx, b_my)) {
packedhit = true;
if (b.propagateMove(b_mx, b_my)) {
interrupted = true;
}
} else {
b.propagateLeave();
}
}
}
// child prevented cascade during _Move/Move which blocks
// Enter on this box - invoking Leave if necessary
if (interrupted) {
if (test(MOUSEINSIDE)) {
if (!test(MOUSEINSIDE_BLOCKED)) {
// mouse previously inside, now blocked so invoke Leave
set(MOUSEINSIDE_BLOCKED);
if (test(LEAVE_TRAP)) {
justTriggerTraps(SC_Leave, JSU.T);
}
}
} else {
// mouse not previously inside, Enter not yet triggered, so
// do not invoke Leave
set(MOUSEINSIDE);
set(MOUSEINSIDE_BLOCKED);
}
// propagate cascade prevention
return true;
}
// set cursor if applicable to this box
if (test(CURSOR)) {
Surface s = getSurface();
if (s!=null && !s.cursorset) {
s.cursor = JSU.toString(getAndTriggerTraps(SC_cursor));
s.cursorset = true;
}
}
// fire Enter traps
if (!test(MOUSEINSIDE)) {
set(MOUSEINSIDE);
if (test(ENTER_TRAP)) {
justTriggerTraps(SC_Enter, JSU.T);
}
}
// finish post-event Move which follows Enter/Leave
if (test(MOVE_TRAP)) {
if (Interpreter.CASCADE_PREVENTED == justTriggerTraps(SC_Move, JSU.T)) {
// propagate cascade prevention
return true;
}
}
// propagation uninterrupted
return false;
}
It depends on the JVM implementation of course, but is probably true for implementations on mainstream CPUs.
If you actually have 32 flags in a class, and a large number of instances of that class, yes. If you never have more than a few hundred instances, it’s not worth worrying about.
This is true.
That depends also on the memory usage. If you work very intensively with only a few objects, the bitwise operations may slow things down. If you have lots of objects, the reduced memory will probably improve performance a lot due to better caching behaviour.
Instead of doing the bitwise operations yourself, you can (and should) use a
BitSet. And yes, it would be even cleaner if you could work with Enums and anEnumSet, but if you have a number of enums with a few elements each, it would probably not yield the desired memory savings, due to the overhead for multipleEnumSetinstances.