I wrote a function doing simple math:
def clamp(num: Double, min: Double, max: Double) =
if (num < min) min else if (num > max) max else num
It is very simple, until I needed the same function with Long type. I generalized it with type parameter and specialization:
import Ordering.Implicits._
def clamp[@specialized N: Ordering](num: N, min: N, max: N) =
if (num < min) min else if (num > max) max else num
It works, but I found that the bytecode does lots of boxing and unboxing under the hood:
public boolean clamp$mZc$sp(boolean num, boolean min, boolean max, Ordering<Object> evidence$1)
{
return Ordering.Implicits..MODULE$.infixOrderingOps(BoxesRunTime.boxToBoolean(num), evidence$1).$greater(BoxesRunTime.boxToBoolean(max)) ? max : Ordering.Implicits..MODULE$.infixOrderingOps(BoxesRunTime.boxToBoolean(num), evidence$1).$less(BoxesRunTime.boxToBoolean(min)) ? min : num;
}
public byte clamp$mBc$sp(byte num, byte min, byte max, Ordering<Object> evidence$1)
{
return Ordering.Implicits..MODULE$.infixOrderingOps(BoxesRunTime.boxToByte(num), evidence$1).$greater(BoxesRunTime.boxToByte(max)) ? max : Ordering.Implicits..MODULE$.infixOrderingOps(BoxesRunTime.boxToByte(num), evidence$1).$less(BoxesRunTime.boxToByte(min)) ? min : num;
}
public char clamp$mCc$sp(char num, char min, char max, Ordering<Object> evidence$1)
{
return Ordering.Implicits..MODULE$.infixOrderingOps(BoxesRunTime.boxToCharacter(num), evidence$1).$greater(BoxesRunTime.boxToCharacter(max)) ? max : Ordering.Implicits..MODULE$.infixOrderingOps(BoxesRunTime.boxToCharacter(num), evidence$1).$less(BoxesRunTime.boxToCharacter(min)) ? min : num;
}
Is there any better way to do generalized arithmetic operations without boxing?
Scala comes with the standard numeric data types you’d expect. In Scala all of these data types are full-blown objects (not primitive data types). These examples show how to declare variables of the basic numeric types:
Bitwise Operators. In Scala, there are 7 bitwise operators which work at bit level or used to perform bit by bit operations. Following are the bitwise operators : Bitwise AND (&): Takes two numbers as operands and does AND on every bit of two numbers. The result of AND is 1 only if both bits are 1.
An Integer is a 32-bit value and is central to any numeric representation in Scala. The Long and Short types are similar to Integer. Let’s see an example of how they’re used: We can see that we can not assign a value that’s beyond the range which a Short can hold.
An Integer is a 32-bit value and is central to any numeric representation in Scala. The Long and Short types are similar to Integer. Let’s see an example of how they’re used:
The spire project is definitely the right place to look for high performance numerical abstractions. All its typeclasses are specialized for common types such as long, double, float, int.
Here is your method using spire typeclasses:
import spire.algebra._
import spire.implicits._
def clamp[@specialized T:Order](a: T, min: T, max: T) =
if(a < min) min else if(a > max) max else a
And here is the specialized bytecode (long
version), extracted using :javap from the scala REPL:
public long clamp$mJc$sp(long, long, long, spire.algebra.Order<java.lang.Object>);
descriptor: (JJJLspire/algebra/Order;)J
flags: ACC_PUBLIC
Code:
stack=5, locals=8, args_size=5
0: aload 7
2: lload_1
3: lload_3
4: invokeinterface #96, 5 // InterfaceMethod spire/algebra/Order.lt$mcJ$sp:(JJ)Z
9: ifeq 16
12: lload_3
13: goto 35
16: aload 7
18: lload_1
19: lload 5
21: invokeinterface #99, 5 // InterfaceMethod spire/algebra/Order.gt$mcJ$sp:(JJ)Z
26: ifeq 34
29: lload 5
31: goto 35
34: lload_1
35: lreturn
As you can see, it is calling the long specialized version of the gt method of spire.algebra.Order, so there is no boxing involved.
You can also notice that the transformation from the operators (< and >) to the typeclass method invocation does not appear in the code. The machinery behind this is quite elaborate. See this blog post from Erik Osheim, one of the main authors of spire.
But the bottom line is that the result is very fast even though the code is generic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With