• 品味性能之道<十一>:JAVA中switch和if性能比较


    通常而言大家普遍的认知里switch case的效率高于if else。根据我的理解而言switch的查找类似于二叉树,if则是线性查找。按照此逻辑推理对于对比条件数目大于3时switch更优,并且对比条件数目越多时switch的优势越为明显。

    一、测试目的

    最近与开发同学对于前面提到的性能问题,有着各自不同的见解,为证明我的观点,现设计如下测试场景验证

    PS:一个方法里多达65个if else


     

    二、测试策略
    利用Junit4执行本次测试,分别设计50个、70个、100个条件式测试,每轮测试分别执行1千万、2千万、3千万、4千万、5千万和6千万次,为了力求让每轮测试不受外部因素干扰每轮测试执行10次收集信息分析。
    为了让java在纯净的环境中运行。同时关闭了QQ、360、chrome等应用软件。 


    三、测试环境
    • Java 版本信息
    Java version "1.7.0_25"
    Java(TM) SE Runtime Environment (build 1.7.0_25-b17)
    Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)
    Junit4
    • JVM配置信息
    --launcher.XXMaxPermSize
    256m
    -Dosgi.requiredJavaVersion=1.6
    -Xms512m
    -Xmx1024m
    • 系统信息
    Windows7 旗舰版
    64位操作系统
    • 设备信息
    处理器:Intel(R) Core(TM) i3-2328M CPU @ 2.20GHz 2.20 GHz
    安装内存(RAM):4.00GB (3.90 GB 可用)
    因此次不涉及硬盘读写,故不记录硬盘信息


    四、测试脚本
            因篇幅所限,测试脚本略有缩减。
     1 import java.util.Calendar;
     2 import java.util.GregorianCalendar;
     3 import java.util.Random;
     4 import org.junit.After;
     5 import org.junit.Before;
     6 import org.junit.Test;
     7 public class ServerServiceTest {
     8  Calendar calender_begin, calender_end;
     9  Long time_begin, time_end; //记录测试开始时间,结束时间
    10  int flagNumber = 1000000;// 迭代数
    11  Random r = new Random();
    12  int i = new Random().nextInt(100);//生成随机种子
    13  @Before
    14  public void setUp() throws Exception {
    15   calender_begin = new GregorianCalendar();
    16   time_begin = calender_begin.getTimeInMillis();
    17  }
    18  @After
    19  public void tearDown() throws Exception {
    20   calender_end = new GregorianCalendar();
    21   time_end = calender_end.getTimeInMillis();
    22   System.out.println(time_end - time_begin);
    23  }
    24  @Test
    25  public void ifTest() {
    26   for (int temp = 0; temp < flagNumber; temp++) {
    27    i = r.nextInt(100);
    28    if (i == 0) {
    29    } else if (i == 1) {
    30    } else if (i == 2) {
    31    } else if (i == 3) {
    32    } else if (i == 4) {
    33    } else if (i == 5) {
    34    } else if (i == 6) {
    35    } else if (i == 7) {
    36    } else if (i == 8) {
    37    } else if (i == 9) {
    38    } else if (i == 10) {
    39    } 
    40   }
    41  }
    42  @Test
    43  public void switchTest() throws InterruptedException {
    44   for (int temp = 0; temp < flagNumber; temp++) {
    45    i = r.nextInt(100);
    46    switch (i) {
    47    case 0:
    48     break;
    49    case 1:
    50     break;
    51    case 2:
    52     break;
    53    case 3:
    54     break;
    55    case 4:
    56     break;
    57    case 5:
    58     break;
    59    default:
    60     break;
    61    }
    62   }
    63  }
    64 }
    View Code 

    五、测试结果
            以下是收集的测试数据,时间单位毫秒(ms)。其实这种数据看起来很难看出问题所在。
            
    条件式测试数 迭代数   1
    (ms)
    2 3 4 5 6 7 8 9 10 avg max min
    100 6千万 if 469 466 474 455 477 478 466 460 464 483 469 483 455
    switch 443 443 441 438 443 437 441 442 439 438 441 443 437
    5千万 if 399 420 394 403 408 402 403 393 410 430 406 430 393
    switch 367 374 370 366 374 382 381 376 373 397 376 397 366
    4千万 if 344 325 326 359 320 325 324 319 319 328 329 359 319
    switch 302 305 300 315 302 302 298 318 297 300 304 318 297
    3千万 if 255 249 240 248 249 247 250 256 251 246 249 256 240
    switch 228 232 227 231 230 229 227 231 228 231 229 232 227
    2千万 if 211 177 183 182 181 172 174 170 175 178 180 211 170
    switch 165 149 155 152 154 155 155 166 151 158 156 166 149
    1千万 if 179 174 176 176 169 177 176 191 173 183 177 191 169
    switch 152 156 167 161 158 151 161 161 159 161 159 167 151
    70 6千万 if 424 416 440 437 427 419 417 411 416 429 424 440 411
    switch 389 395 387 388 388 392 397 391 392 393 391 397 387
    5千万 if 368 366 352 354 351 352 350 362 355 361 357 368 350
    switch 327 327 326 324 328 327 324 323 330 325 326 330 323
    4千万 if 321 300 295 293 284 283 281 335 276 281 295 335 276
    switch 259 262 260 262 259 261 259 268 260 267 262 268 259
    3千万 if 219 229 226 217 220 226 215 223 217 226 222 229 215
    switch 199 197 203 199 199 199 197 200 200 197 199 203 197
    2千万 if 149 158 152 155 177 159 159 158 161 150 158 177 149
    switch 136 136 132 134 145 133 133 132 136 133 135 145 132
    1千万 if 86 83 87 81 90 88 77 83 95 85 86 95 77
    switch 65 67 67 67 68 71 67 68 68 68 68 71 65
    50 6千万 if 374 361 363 363 362 364 376 366 372 373 367 376 361
    switch 347 343 341 341 338 362 340 343 343 343 344 362 338
    5千万 if 324 312 306 306 341 312 312 299 307 307 313 341 299
    switch 289 287 285 283 291 288 290 288 290 281 287 291 281
    4千万 if 287 247 251 252 265 247 248 256 252 256 256 287 247
    switch 239 237 236 229 243 230 235 232 228 228 234 243 228
    3千万 if 193 196 195 197 203 198 201 188 200 204 198 204 188
    switch 184 178 181 175 173 172 176 184 193 174 179 193 172
    2千万 if 128 129 133 145 133 139 139 130 131 143 135 145 128
    switch 117 118 118 117 115 120 114 113 116 118 117 120 113
    1千万 if 81 68 82 75 76 68 69 79 91 75 76 91 68
    switch 60 57 60 60 59 65 59 62 61 60 60 65 57


    六、测试结果分析
            纯数据的测试结果,很难进行分析,经过整理以后如下图:
            
     
            if-100为if执行100条件式测试数,switch-100为switch执行100条件式测试数;
            根据此图表结果,大家已经可能很清晰的看出IfSwtich的性能对比结果了。但是如此细微的性能差异,实现了业务就行了,何必关注这种费心又麻烦的事呢?
            哈哈哈,性能测试更多时候,也是沟通问题,更是行政问题。

     
    七、总结
            这次验证过程,其实就是一次简单的性能测试过程,也就是——需求挖掘->明确目的->设计策略->准备环境->脚本编写->收集数据->结果分析->测试报告。此处略去的报告内容,因为不需要什么报告了。哈哈哈!(大家懂的)
            就大量条件式的业务场景而言,除了利用switch以外,其实还可以用到枚举(enum)作为条件式,抽象每个判断式导向为函数式(function)。可能哪天我心情好会把利用enum优化的代码给放出来。
     

    2015-8-23 15:17:18 跟新
    策略模式+接口注入,写的是伪代码没有严格的语法规范,大家凑合着看。
    interface Service{
    	
    	public void execute();
    
    	public <T> T eval();
    }
    
    
    public class Strategy{
    
    	private static Concrunthashmap<String,function> content = new Concrunthashmap<String,function>();
    
    	public void register(String name,Clas<T> xxx){
    		if(!content.has(name)){
    			content.put(xxx);//这里隐去了反射生成对象的过程
    		}
    	}
    
    	public void execute(String name){
    		content.get(name).execute();
    	}
    
    	public void eval(String name){
    		content.get(name).eval();
    	}
    }
    
    class HelloServiceImpl implements Service{
    
    	public void execute(){
    		print "hello world";
    	}
    
    	public String eval(){
    		return "hello world";
    	}
    }
    
    class HiServiceImpl implements Service{
    
    	public void execute(){
    		print "hi world";
    	}
    
    	public String eval(){
    		return "hi world";
    	}
    }
    
     
  • 相关阅读:
    HDU 5313 bitset优化背包
    bzoj 2595 斯坦纳树
    COJ 1287 求匹配串在模式串中出现的次数
    HDU 5381 The sum of gcd
    POJ 1739
    HDU 3377 插头dp
    HDU 1693 二进制表示的简单插头dp
    HDU 5353
    URAL 1519 基础插头DP
    UVA 10294 等价类计数
  • 原文地址:https://www.cnblogs.com/snifferhu/p/3500864.html
Copyright © 2020-2023  润新知