[CLJ-740] Unnecessary boxing of primitives in case form Created: 17/Feb/11 Updated: 01/Mar/11 |
|
| Status: | Open |
| Project: | Clojure |
| Component/s: | None |
| Affects Version/s: | Release 1.3 |
| Fix Version/s: | None |
| Type: | Defect | Priority: | Major |
| Reporter: | Mikhail Kryshen | Assignee: | Unassigned |
| Resolution: | Unresolved | Votes: | 0 |
| Labels: | None | ||
| Description |
|
Found this while profiling some performance-critical code. Consider the following Clojure function: (defn test-case ^double [^long i ^double d1 ^double d2] (case (int i) 0 d1 d2)) Current Clojure 1.3 snapshot compiles it to: public final double invokePrim(long, double, double) throws java.lang.Exception; Code: 0: lload_1 1: invokestatic #67; //Method clojure/lang/RT.intCast:(J)I 4: istore 7 6: iload 7 8: i2l 9: invokestatic #73; //Method clojure/lang/Numbers.num:(J)Ljava/lang/Number; 12: invokestatic #79; //Method clojure/lang/Util.hash:(Ljava/lang/Object;)I 15: iconst_0 16: ishr 17: iconst_1 18: iand 19: tableswitch{ //0 to 0 0: 36; default: 58 } 36: iload 7 38: i2l 39: invokestatic #73; //Method clojure/lang/Numbers.num:(J)Ljava/lang/Number; 42: getstatic #45; //Field const__3:Ljava/lang/Object; 45: invokestatic #83; //Method clojure/lang/Util.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z 48: ifeq 58 51: dload_3 52: invokestatic #88; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double; 55: goto 63 58: dload 5 60: invokestatic #88; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double; 63: checkcast #92; //class java/lang/Number 66: invokevirtual #96; //Method java/lang/Number.doubleValue:()D 69: dreturn } This bytecode contains boxing of primitives (calls to clojure/lang/Numbers.num and java/lang/Double.valueOf) and calls to clojure/lang/Util.hash and clojure/lang/Util.equals that does not seem necessary. At 60-66 primitive double is boxed into Double only to be converted back into primitive. The equivalent Java code compiles to much simpler and faster bytecode: public double testCase(long, double, double); Code: 0: lload_1 1: l2i 2: lookupswitch{ //1 0: 20; default: 22 } 20: dload_3 21: dreturn 22: dload 5 24: dreturn } |
| Comments |
| Comment by Alexander Taggart [ 28/Feb/11 2:16 PM ] |
|
Improved via patch on (defn test-case ^double [^long i ^double d1 ^double d2]
(case (int i)
0 d1
d2))
now emits as 0 lload_1 [i]
1 invokestatic clojure.lang.RT.intCast(long) : int [67]
4 istore 7 [G__7903] // let-bound expression
6 iload 7 [G__7903]
8 tableswitch default: 32
case 0: 28
28 dload_3 [d2]
29 goto 34
32 dload 5 [arg2]
34 dreturn
or if the int cast of the expression is omitted: 0 lload_1 [i]
1 lstore 7 [G__7903] // let-bound expression
3 lload 7 [G__7903]
5 l2i
6 tableswitch default: 35
case 0: 24
24 lconst_0 // match, verify long expr wasn't truncated
25 lload 7 [G__7903]
27 lcmp
28 ifne 35
31 dload_3 [d2]
32 goto 37
35 dload 5 [arg2]
37 dreturn
|