For those unfamiliar with bitwise operators and bit shifts, Wikipedia is a more than sufficient introduction to the topic. For useful tips on how bitwise operators and bitshifts can be utilized, the site Bit Twiddling Hacks and the book Hacker's Delight are great resources.
In the past, bitwise operators and bit shifts were used out of pure necessity. Their use was justified mostly by the performance gains they brought about for the severely underpowered, resource-constrained machines of the time. However, in this day and age when , their exact purpose is somewhat difficult to determine, especially considering how much better compilers are these days at what they do. In most cases, it is very unlikely that bitwise operators will result in an increase in efficiency that is significant enough to warrant their use. Additionally, bitwise operators pose a serious threat to the readability of a program. It is all too easy for a clever and overzealous programmer to obfuscate some code by exploiting unnecessary 'bit-twiddling hacks'.
Also, even though bitwise operators are - at least in principle - largely 'language agnostic', there's still the fact that they are legacies of a much older set of languages (Assembly, C). As a result, while they may be accessible in more modern high-level languages like Java and Python, the consequences of their use may not be similar, at least in spirit. For example, in Java, the JVM adds an extra layer that makes it difficult for precise optimizations to be made. The final performance of a piece of code is largely dependent on the internal architecture of the JVM on which it is ran, and this, evidently, varies from vendor to vendor. Having this aspect of a project, not being under the auspices of the programmer makes it difficult to make any sort of sensible prediction as to what kind of performance hit you can expect when using regular operators as opposed to bitwise operators.
Anyways, the purpose of this entry was not to venerate or lambast bitwise operators as such, but rather to examine whether or not they could be used in the JSBML math package.
The simulation of biological models typically results in the performance of repetitive arithmetic operations, numerous times. Individually these operations have an insignificant time cost, but performed a lot and performed repetitively, these times may stack up to a significant cost. In a previous entry we looked at how performance is affected when class identity detection is handled through instanceof as opposed to the regular relational equality operator. As is typically the case with tests of this sort, they turned out inconclusive results.
Overall, the role of instanceof or any of its more syntax heavy siblings was merely incidental. Neither play an actual role in the calculations involved. It is an operation that is tied to the Java language. Compared to the previous approach, the new approach is more language agnostic. In order to validate any improvements brought about by the use of bitwise operators, the following, hopefully representative tests were done:
import java.util.concurrent.TimeUnit;
public class BitwiseBenchmark {
/**
* Comparing times with bitwise operators
* and without bitwise operators
*
* @param String[] args
*/
public static void main(String[] args) {
long a = 1, c = 0, start = 0, end = 0, bitwiseLength = 0,
operatorLength = 0, MAX = Long.parseLong(args[0]),
MAX2 = Long.parseLong(args[1]);
for (int b = 0; b < MAX2; b++) {
for (int j = 0; j < MAX; j++) {
start = System.nanoTime();
c = a << 1;
end = System.nanoTime();
operatorLength += end - start;
}
a = 1;
for (int i = 0; i < MAX; i++) {
start = System.nanoTime();
c = a * 2;
end = System.nanoTime();
bitwiseLength += end - start;
}
}
String results = String.format("\tRESULTS\t\nBitwise: %dms\nOperator: %dms\n",
TimeUnit.NANOSECONDS.toMillis(bitwiseLength/MAX2),
TimeUnit.NANOSECONDS.toMillis(operatorLength/MAX2));
System.out.print(results);
}
}
Ran with this command:
java BitwiseBenchmark 100000000 40
To give these results:
RESULTS Bitwise: 6248ms Operator: 5798msOn face value, these results are enough to firmly discredit any claims that bitwise operators are a better, more efficient alternative to regular operators. However, from past experience, I know that there's more that's going on here, than meets the eye. Flipping the positioning of the two loops for example, results in the regular operator taking a longer time than the bitwise operator. This is an issue that I encountered when I first tried to test the instanceof operator, and there's still no clear explanation for it. Again, I suspect that it has something to do with the JVM, but since this is not my area of expertise, there's really not much I can say aside from that.
Ultimately, the conclusions I've been able to draw from my readings and doing this little experiment are that (1) Manipulating bits directly should be avoided, unless there is clear justification for using them. This 'clear justification' may take the form of a necessary but probably marginal increase in efficiency, for which a fall in the readability of the code is a small price to pay. (2) Efficiency gains from using bitwise operators are insignificant in most cases.