Skip to content

How Erjang handles BigNums

krestenkrab edited this page Sep 13, 2010 · 17 revisions

All the JVM-based languages that have automatic overflow from integers to BigInteger have significant performance overhead for doing integer arithmetics. This applies for JRubi, Ioke, Clojure, and so on.

So in the general case, computing val1 + val2 involves a double-dispatch to reach a function that can compute + for the specific type of the two operands. The code below illustrates how the double dispatch happens (with code for two integers) and the overflow check that will have to happen. The computation happens with 64-bit integers, and the overflow is a range check.

This will have a performance overhead, time wil show how bad it is… I don’t personally think that the extra instructions required is the big cost, the cost is in heap allocating a new ESmall for every integer arithmetic.

The body of ESmall.r_plus below is almost a verbatim copy of the same code in Clojure. This variant however runs faster because it does not have to do instanceof or casts, whcih is possible because Erjang objects all inherit from a class that I control. In clojure such a shared super class is not assumed.

// do plus
static ENumber plus(EObject n1, EObject n2) {
  return n1.plus(n2); 
}
// Any erlang object
class EObject {
   @BIF("+")
   ENumber plus(EObject rhs) { throw ERT.badarg(this,rhs); }
   ENumber r_plus(int lhs) { throw ERT.badarg(lhs,this); }
   ENumber r_plus(BigInteger lhs) { throw ERT.badarg(lhs,this); }
   ENumber r_plus(double lhs) { throw ERT.badarg(lhs,this); }
}
 // Representation of int32
class ESmall extends ENumber {
   int value;
   ENumber plus(EObject rhs) { return rhs.r_plus(value); }
   ENumber r_plus(int lhs) {  
        long ret = (long)lhs + this.value;

        // can we convert ret to 32-bit and back to 64-bit without loss?
	if(ret == (long)(int)ret)
		return new ESmall((int)ret);
	else      
		return new EBig(ret);
   }

   // and others ... 
}

The reality is slightly more complicated than conveyed here, … but you get the general idea. Guard BIFs have separate implementations, they return null on failure.

Clone this wiki locally