Total Articles 494
C++에서는 잘 만들어 썼었는데, Java 프로그래밍을 하면서도 비슷한 모듈이 있으면 좋겠다는 생각에 만들어 보았습니다.
아래 코드를 보면 코드 실행의 흐름을 판단하기 쉬우며, doSomething이라는 메소드가 각각 수행되는데 걸리는 시간도 어려지 않게 예측할 수 있습니다.
public class Main { static void doSomething(long millis) { try { Thread.sleep(millis); } catch(Exception e) {} } public static void main(String[] args) { for (int i = 0; i < 10; i++) { doSomething(10); doSomething(20); doSomething(30); doSomething(40); } } }
하지만, 코드의 양도 많아 지고 분기도 복잡하 지고 하다 보면, 나중에 가서는 어느 부분에서 시간이 많이 걸리는지 코드상으로는 판별하기 어려워 질 수가 있죠. 프로그래머는 어느 부분에서 시간이 많이 걸리는지를 알고 싶어 합니다. 이러한 경우 Performance라는 클래스를 이용하여 코드의 중간중간 수행 시간을 알아 낼 수 있습니다.
1. Performance 클래스의 오브젝트를 생성한다.
2. 코드의 중간중간에 check(int milestone) 메소드를 호출하여 이정표를 남겨 놓는다.
3. 나중에 결과를 알고 싶을 때 report() 메소드를 호출하여 결과를 본다.
상기 코드에서 Performance 클래스를 이용해서 중간중간에 코드를 삽입합니다(빨간색입니다).
public class Main { static void doSomething(long millis) { try { Thread.sleep(millis); } catch(Exception e) {} } public static void main(String[] args) { Performance pf = new Performance(); // Performace 객체 생성 for (int i = 0; i < 10; i++) { pf.check(1); // 코드 중간중간에 milestone을 남겨 놓음 doSomething(10); pf.check(2); // 코드 중간중간에 milestone을 남겨 놓음 doSomething(20); pf.check(3); // 코드 중간중간에 milestone을 남겨 놓음
doSomething(30); pf.check(4); // 코드 중간중간에 milestone을 남겨 놓음
doSomething(40); } pf.report(); // 마지막에 실행 결과를 출력 } }
실행 결과입니다.
beg end count duration
1 2 10 100371975 // 대략 100 msec (10번 * 10 msec)
4 1 9 360059588 // 대략 360 msec (9번 * 40 msec)
2 3 10 200130182 // 대략 200 msec (10번 * 20 msec)
3 4 10 300004752 // 대략 300 msec (10번 * 30 msec)
1, 2, 3, 4 라는 이정표들 사이에 지나간 횟수와 걸린 시간을 알 수가 있습니다.
verbose 옵션을 true로 해 주면 나중에 무지막지한 결과를 볼 수 있습니다.
Performance pf = new Performance(); pf.verbose = true;...
beg end duration1 2 104685102 3 201478823 4 298980034 1 399381071 2 100474362 3 199710443 4 299903344 1 399560561 2 100482742 3 200005173 4 299989944 1 399908371 2 100663632 3 199785173 4 299792994 1 400668941 2 99629982 3 199804733 4 300247654 1 400171671 2 99675382 3 200081303 4 300243464 1 399474661 2 100373092 3 199937423 4 300417374 1 399675101 2 100116782 3 199669233 4 300327274 1 400538341 2 99949162 3 200417933 4 297016104 1 402336051 2 100030872 3 200286633 4 29999692
이상 허접 클래스 소개를 마칩니다. ^^
[Performance.java 소스 코드]
// ---------------------------------------------------------------------------- // // VDream Component Suite version 8.0 // // http://www.gilgil.net // // Copyright (c) Gilbert Lee All rights reserved // // ---------------------------------------------------------------------------- import java.util.ArrayList; import java.util.HashMap; // ---------------------------------------------------------------------------- // PerformanceVerbose // ---------------------------------------------------------------------------- class PerformanceVerbose { public int begMilestone; public int endMilestone; public long duration; } //---------------------------------------------------------------------------- // PerformanceVerboseList //---------------------------------------------------------------------------- class PerformanceVerboseList extends ArrayList<PerformanceVerbose> { private static final long serialVersionUID = 1L; } //---------------------------------------------------------------------------- // PerformanceReportKey //---------------------------------------------------------------------------- class PerformanceReportKey { public int begMilestone; public int endMileseone; public int hashCode() { return begMilestone + endMileseone; } public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (!(obj instanceof PerformanceReportKey)) return false; PerformanceReportKey rhs = (PerformanceReportKey)obj; if (this.begMilestone != rhs.begMilestone) return false; if (this.endMileseone != rhs.endMileseone) return false; return true; } } //---------------------------------------------------------------------------- // PerformanceReportData //---------------------------------------------------------------------------- class PerformanceReportData { public int count; public long totalDuration; } //---------------------------------------------------------------------------- // PerformanceReportMap //---------------------------------------------------------------------------- class PerformanceReportMap extends HashMap<PerformanceReportKey, PerformanceReportData> { private static final long serialVersionUID = 1L; } //---------------------------------------------------------------------------- // Performance //---------------------------------------------------------------------------- public class Performance { public boolean verbose; public PerformanceVerboseList verboseList; public PerformanceReportMap reportMap; protected int lastMilestone; protected long lastTick; public Performance() { verbose = false; verboseList = new PerformanceVerboseList(); reportMap = new PerformanceReportMap(); clear(); } public void report() { if (verbose) { System.out.println("beg\tend\tduration"); for (PerformanceVerbose verbose : verboseList) { if (verbose.begMilestone == 0) continue; System.out.println(String.format("%d\t%d\t%d", verbose.begMilestone, verbose.endMilestone, verbose.duration)); } } else { System.out.println("beg\tend\tcount\tduration"); for (PerformanceReportKey key : reportMap.keySet()) { if (key.begMilestone == 0) continue; PerformanceReportData data = reportMap.get(key); System.out.println(String.format("%d\t%d\t%d\t%d", key.begMilestone, key.endMileseone, data.count, data.totalDuration)); } } } public void clear() { verboseList.clear(); reportMap.clear(); lastMilestone = 0; lastTick = System.nanoTime(); } public void check(int milestone) { check(milestone, System.nanoTime()); } public void check(int milestone, long now) { if (verbose) { PerformanceVerbose verbose = new PerformanceVerbose(); verbose.begMilestone = lastMilestone; verbose.endMilestone = milestone; verbose.duration = now - lastTick; verboseList.add(verbose); } else { PerformanceReportKey key = new PerformanceReportKey(); key.begMilestone = lastMilestone; key.endMileseone = milestone; PerformanceReportData data = reportMap.get(key); if (data == null) { data = new PerformanceReportData(); reportMap.put(key, data); } data.count++; data.totalDuration += now - lastTick; } lastMilestone = milestone; lastTick = now; } }
[다운로드]
1. package는 어떻게 해야 할지 몰라서 일단 설정해 놓지 않음.
2. 이정표의 type을 int로 했는데, 향후 template로 구현해 다양한 type을 지원하면 편리할 것 같음.
3. PerformanceReportKey.hashCode 메소드의 충돌 가능성이 높아서 성능에 좋지 않을 수 있음.
4. 현재 버전은 스레드 안정적이지는 않음.