What is the relative performance difference of if/else versus switch statement in Java?
担心我的Web应用程序的性能,我想知道"if/else"或switch语句中哪一个性能更好?
我完全同意这样的观点:过早的优化是应该避免的。
但是,Java VM确实有特殊的字节码,可以用于Switter()。
参见WM规范(查找开关和表开关)
因此,如果代码是性能CPU图的一部分,那么可能会有一些性能提升。
这是微观优化和过早优化,这是邪恶的。而是担心所讨论代码的可读性和可维护性。如果有两个以上的
或者,您也可以获取多态性。首先创建一些接口:
掌握一些
最后,用这样的方法替换
1 | actions.get(name).execute(input); |
它可能比
在讨论Web应用程序时,您可以使用
- 使用面向定制servlet的框架,servlet太多,这是一个问题吗?
- Java前端控制器
如果你担心JavaEE Web应用程序的性能,那么你可能会发现这篇文章也是有用的。还有一些区域比只优化微生成器Java代码更能提高性能。
一个if/else或一个开关极不可能成为性能问题的根源。如果您有性能问题,您应该首先进行性能分析分析,以确定慢点在哪里。过早的优化是万恶之源!
然而,有可能讨论开关与V/R的相对性能,以及Java编译器的优化。首先请注意,在Java中,开关语句在一个非常有限的域——整数上运行。通常,您可以按如下方式查看switch语句:
1 2 3 4 5 6 7 | switch (<condition>) { case c_0: ... case c_1: ... ... case c_n: ... default: ... } |
其中
如果这个集合是"密集的"——也就是说,(max(ci)+1-min(ci)/n>&alpha;,其中0
k大于某个经验值,则可以生成一个跳跃表,这是非常有效的。 如果这个集合不是很密集,但是n>=&beta;,一个二进制搜索树可以在o(2*log(n))中找到目标,这也是有效的。
对于所有其他情况,switch语句的效率与if/else语句的等效系列完全相同。&alpha;和&beta;的精确值取决于许多因素,并由编译器的代码优化模块确定。
最后,当然,如果
使用开关!
我不想再坚持下去了!进行测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | public class SpeedTestSwitch { private static void do1(int loop) { int temp = 0; for (; loop > 0; --loop) { int r = (int) (Math.random() * 10); switch (r) { case 0: temp = 9; break; case 1: temp = 8; break; case 2: temp = 7; break; case 3: temp = 6; break; case 4: temp = 5; break; case 5: temp = 4; break; case 6: temp = 3; break; case 7: temp = 2; break; case 8: temp = 1; break; case 9: temp = 0; break; } } System.out.println("ignore:" + temp); } private static void do2(int loop) { int temp = 0; for (; loop > 0; --loop) { int r = (int) (Math.random() * 10); if (r == 0) temp = 9; else if (r == 1) temp = 8; else if (r == 2) temp = 7; else if (r == 3) temp = 6; else if (r == 4) temp = 5; else if (r == 5) temp = 4; else if (r == 6) temp = 3; else if (r == 7) temp = 2; else if (r == 8) temp = 1; else if (r == 9) temp = 0; } System.out.println("ignore:" + temp); } public static void main(String[] args) { long time; int loop = 1 * 100 * 1000 * 1000; System.out.println("warming up..."); do1(loop / 100); do2(loop / 100); System.out.println("start"); // run 1 System.out.println("switch:"); time = System.currentTimeMillis(); do1(loop); System.out.println(" -> time needed:" + (System.currentTimeMillis() - time)); // run 2 System.out.println("if/else:"); time = System.currentTimeMillis(); do2(loop); System.out.println(" -> time needed:" + (System.currentTimeMillis() - time)); } } |
我的C标准基准代码
我记得读过Java字节码中有2种开关语句。(我认为它是在Java性能调整中的一个),它是一个非常快的实现,它使用Switter语句的整数值来知道要执行的代码的偏移量。这将要求所有整数都是连续的,并且在一个定义良好的范围内。我猜想使用枚举的所有值也将属于该类别。
不过,我同意其他许多海报…除非这是非常热门的代码,否则担心这一点可能还为时过早。
根据他的2009 Java语言中的克里夫点击,一个现代硬件的速成课程:
Today, performance is dominated by patterns of memory access. Cache misses dominate – memory is the new disk. [Slide 65]
你可以在这里看到他的完整幻灯片。
Cliff给出了一个例子(在第30张幻灯片上完成),表明即使CPU进行寄存器重命名、分支预测和推测性执行,它也只能在4个时钟周期内启动7个操作,然后由于两个缓存未命中(需要300个时钟周期才能返回)而不得不阻塞。
所以他说,为了加快程序的速度,你不应该考虑这类小问题,而应该考虑更大的问题,比如是否要进行不必要的数据格式转换,比如转换"soap→xml→dom→sql→……",它"将所有数据通过缓存传递"。
在我的测试中,更好的性能是Enum>Map>Switch>if/else if in windows7。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | import java.util.HashMap; import java.util.Map; public class StringsInSwitch { public static void main(String[] args) { String doSomething = null; //METHOD_1 : SWITCH long start = System.currentTimeMillis(); for (int i = 0; i < 99999999; i++) { String input ="Hello World" + (i & 0xF); switch (input) { case"Hello World0": doSomething ="Hello World0"; break; case"Hello World1": doSomething ="Hello World0"; break; case"Hello World2": doSomething ="Hello World0"; break; case"Hello World3": doSomething ="Hello World0"; break; case"Hello World4": doSomething ="Hello World0"; break; case"Hello World5": doSomething ="Hello World0"; break; case"Hello World6": doSomething ="Hello World0"; break; case"Hello World7": doSomething ="Hello World0"; break; case"Hello World8": doSomething ="Hello World0"; break; case"Hello World9": doSomething ="Hello World0"; break; case"Hello World10": doSomething ="Hello World0"; break; case"Hello World11": doSomething ="Hello World0"; break; case"Hello World12": doSomething ="Hello World0"; break; case"Hello World13": doSomething ="Hello World0"; break; case"Hello World14": doSomething ="Hello World0"; break; case"Hello World15": doSomething ="Hello World0"; break; } } System.out.println("Time taken for String in Switch :"+ (System.currentTimeMillis() - start)); //METHOD_2 : IF/ELSE IF start = System.currentTimeMillis(); for (int i = 0; i < 99999999; i++) { String input ="Hello World" + (i & 0xF); if(input.equals("Hello World0")){ doSomething ="Hello World0"; } else if(input.equals("Hello World1")){ doSomething ="Hello World0"; } else if(input.equals("Hello World2")){ doSomething ="Hello World0"; } else if(input.equals("Hello World3")){ doSomething ="Hello World0"; } else if(input.equals("Hello World4")){ doSomething ="Hello World0"; } else if(input.equals("Hello World5")){ doSomething ="Hello World0"; } else if(input.equals("Hello World6")){ doSomething ="Hello World0"; } else if(input.equals("Hello World7")){ doSomething ="Hello World0"; } else if(input.equals("Hello World8")){ doSomething ="Hello World0"; } else if(input.equals("Hello World9")){ doSomething ="Hello World0"; } else if(input.equals("Hello World10")){ doSomething ="Hello World0"; } else if(input.equals("Hello World11")){ doSomething ="Hello World0"; } else if(input.equals("Hello World12")){ doSomething ="Hello World0"; } else if(input.equals("Hello World13")){ doSomething ="Hello World0"; } else if(input.equals("Hello World14")){ doSomething ="Hello World0"; } else if(input.equals("Hello World15")){ doSomething ="Hello World0"; } } System.out.println("Time taken for String in if/else if :"+ (System.currentTimeMillis() - start)); //METHOD_3 : MAP //Create and build Map Map<String, ExecutableClass> map = new HashMap<String, ExecutableClass>(); for (int i = 0; i <= 15; i++) { String input ="Hello World" + (i & 0xF); map.put(input, new ExecutableClass(){ public void execute(String doSomething){ doSomething ="Hello World0"; } }); } //Start test map start = System.currentTimeMillis(); for (int i = 0; i < 99999999; i++) { String input ="Hello World" + (i & 0xF); map.get(input).execute(doSomething); } System.out.println("Time taken for String in Map :"+ (System.currentTimeMillis() - start)); //METHOD_4 : ENUM (This doesn't use muliple string with space.) start = System.currentTimeMillis(); for (int i = 0; i < 99999999; i++) { String input ="HW" + (i & 0xF); HelloWorld.valueOf(input).execute(doSomething); } System.out.println("Time taken for String in ENUM :"+ (System.currentTimeMillis() - start)); } } interface ExecutableClass { public void execute(String doSomething); } // Enum version enum HelloWorld { HW0("Hello World0"), HW1("Hello World1"), HW2("Hello World2"), HW3( "Hello World3"), HW4("Hello World4"), HW5("Hello World5"), HW6( "Hello World6"), HW7("Hello World7"), HW8("Hello World8"), HW9( "Hello World9"), HW10("Hello World10"), HW11("Hello World11"), HW12( "Hello World12"), HW13("Hello World13"), HW14("Hello World4"), HW15( "Hello World15"); private String name = null; private HelloWorld(String name) { this.name = name; } public String getName() { return name; } public void execute(String doSomething){ doSomething ="Hello World0"; } public static HelloWorld fromString(String input) { for (HelloWorld hw : HelloWorld.values()) { if (input.equals(hw.getName())) { return hw; } } return null; } } //Enum version for betterment on coding format compare to interface ExecutableClass enum HelloWorld1 { HW0("Hello World0") { public void execute(String doSomething){ doSomething ="Hello World0"; } }, HW1("Hello World1"){ public void execute(String doSomething){ doSomething ="Hello World0"; } }; private String name = null; private HelloWorld1(String name) { this.name = name; } public String getName() { return name; } public void execute(String doSomething){ // super call, nothing here } } /* * http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string * https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10 * http://forums.xkcd.com/viewtopic.php?f=11&t=33524 */ |
对于大多数
但问题是:如果您使用的是
与