시소당
Article topic :
OPTIMIZING STRING PERFORMANCE
Article Content :
여기서 제가 제시하고자 할 내용은 프로그램을 짜는 여러분이 Integer 라는 wrapper object의 list를 가지고 있을 경우에 java 로 application 프로그래밍에 관련된 것을 다룰것이다.
이러한 list를 문자열로 변환시키는 것을 프로그래밍시 너무나도 빈번하게 일어나는 일 중의 하나일것이다.
다른 말로 표현하자면 application프로그램작성시 아래와 같은 코드를 볼수가 있다.
List list = new ArrayList();
list.add(new Integer(37));
list.add(new Integer(47));
...
String s = list.toString();
toString()메소드는 해당 list를 문자열형태로 변환해 주는데, 출력되는 결과형태는 아래와 같다.
[37, 47]
자.. 이제 어떤 방법을 이용해서 스트링으로 변환시 최적화시킬수 있을까?
이 질문에 대한 답은 list를 string으로 변환시 내부적으로 어떤일이 발생되는 가를 보면 유용하다.
내부적으로 list출력하기 위하여 StringBuffer의 객체를 하나 생성한후 각각의 List의 element들을 스트링으로 변환하여 buffer에 추가시키는 일을 한다.
마지막으로 이 StringBuffer객체를 String 객체로 변환시키는 일을 한다.
List의 원소들은 메소드를 연속적으로 호출함으로서 각각의 원소를 string으로 변환시키는데, 여기서 가장 중요한 사실은 Integer클래는 Integer.toString(value, radix)를 통해서 변환시킨다는 점이다.
이 메소드는 59라는 숫자값을 새로운 스트링 형태로 변환시키는 일을 한다.
변환작업은 내부적으로 각각의 자리수가 가지고 있는 숫자형태의 값을 하나하나 추출하여 스트링표현에 맞게끔 표현해 주어야 하기때문에 매우 많은 비용이 든다.
기본적인 접근으로 좀더 효과적이 될수 있을까?
몇몇가지 경우에 있어서 이 질문에 대한 답은 "yes"이다
예제로 0부터 99사이의 범위이 값을 가지는 Integer의 예를 들어보겠다.
이경우 toString메소드를 local version으로 만들어 낼수 있는데 이경우 API의 toString()메소드보다 수행속도가 빠르다.
그 코드를 보도록 하자.
import java.util.*;
public class PerfDemo {
// ArrayList 객체의 길이 초기화
static final int MAXLISTLEN = 10000;
// Integer object의 최대값
static final int MAXNUM = 99;
// integers값들의 cache배열
static String cache[] = new String[MAXNUM + 1];
// 캐쉬배열을 채운다.
static {
for (int i = 0; i <= MAXNUM; i++) {
cache
= Integer.toString(i);
}
}
// 캐쉬를 이용한 toString메소드의 local version을 만들었다.
static String localtoString(List list) {
StringBuffer sb = new StringBuffer();
sb.append("[");
int size = list.size();
for (int i = 0; i < size; i++) {
Integer iobj = (Integer)list.get(i);
sb.append(cache[iobj.intValue()]);
if (i + 1 < size) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
public static void main(String args[]) {
Random rn = new Random(0);
List list = new ArrayList();
// fill list with random Integer values 0-99
for (int i = 1; i <= MAXLISTLEN; i++) {
int r = rn.nextInt(MAXNUM + 1);
list.add(new Integer(r));
}
String s1 = null;
String s2 = null;
// 기본적인 toString()메소드에 의한 접근
long currtime = System.currentTimeMillis();
for (int i = 1; i <= 100; i++) {
s1 = list.toString();
}
long elapsed = System.currentTimeMillis() - currtime;
System.out.println("standard toString time = " +
elapsed);
// 새로 만든 메소드에 의한 접근
currtime = System.currentTimeMillis();
for (int i = 1; i <= 100; i++) {
s2 = localtoString(list);
}
elapsed = System.currentTimeMillis() - currtime;
System.out.println("local toString time = " + elapsed);
// check to make sure same strings are produced
if (!s1.equals(s2)) {
System.out.println("error");
}
}
}
위 프로그램을 설명하면 초기에 스트링캐쉬에 배열을 선언해 값을 0부터 99까지 할당한후 toString()메소드로 출력하는 시간과 직접 toString()메소드를 구현한것처럼 메소드를 작성하여 100번을 찍는 시간값을 체크해봤다.
캐쉬를 사용한 경우는 각각의 Integer객체를 intValue()메소드를 호출함으로써 간단하게 구현했으며 캐쉬테이블의 인덱스값을 사용하여 테스트했다
이프로그램을 실행시키면 다음과 같은 결과값이 나올것이다.
standard toString time = 4206
local toString time = 2554
Press any key to continue...
테스트 환경은 Windows 2000, P-III 500, 256MRAM을 사용하여 테스트한 결과이다.
시간단위는 milli초이다.
Article report :
위 결과는 로컬메소드로 사용된 것이 실제 API의 toString()메소드보다 두배정도가 빠른것을 보이고 있다.
이러한 이유는 로컬메소드가 integer를 string으로 변환시키는것과 String객체생성하는 중간단계를 없앴기 때문에 나타나는 결과이다.
이해가 좀 되는가?
사실 최적화는 일반적인 것이 아니다.아무리 숫자값들이 범위를 캐슁기술에 의해 확장을 할수만은 없는 일이며, 결국 속도를 위하여 메모리 공간과 바꾸어야 하는 일이 발생하게 된다.
캐쉬테이블이 너무 커진다면 그건 터무니 없는 프로그램이 되고 말것이다.
하지만 많은 최적화에 관련된 내용들이 이러한 형태를 사용하고 있다.
어떤 특별한 데이터타입이나 애플리케이션의 operation에 대한 기반을 개척해 나가야
성능에 관련된 문제를 해결해나갈수 있을것이다.
작성자명 : 최지웅(ienvyou@orgio.net)
출처 : http://www.ibm.com//developerworks/kr/forums/dw_thread.jsp?forum=7&thread=847&message=1148&cat=4&q=%EB%A9%94%EB%AA%A8%EB%A6%AC#1148