일요일, 9월 24, 2006

[Java] 자바에서의 File IO 성능

파일을 읽고 쓰는 것은 프로그래밍에서 아주 빈번하게 발생하는 일이다.

일반적인 통념 상 파일 시스템은 네트웍에 비해 훨씬 빠르고 메모리에 비해 훨씬 느리다. 보통 그 성능 비율은 100 정도로 유지가 되고 있다.

파일을 읽고 쓸 때의 성능을 보장하려면 다음 몇 가지 부분을 염두에 둬야 한다.

  • file open/close 가 빈번하게 발생하면 성능에 나쁜 영향을 미친다.
  • output stream flush가 빈번하게 발생하면 성능에 나쁜 영향을 미칠 수 있다. (file의 경우 윈도우에서는 거의 영향이 없었고, 리눅스의 경우 큰 영향이 있었다.)
  • stream io와 channel io를 단순 비교하면 stream io 성능이 오히려 나은 경우가 대부분이다.
  • buffered io를 사용하면 어느 정도의 성능 개선 효과를 보인다. (리눅스 output stream의 경우 아주 큰 개선 효과. 다른 경우에도 조금의 성능 개선 효과. 물론 최적의 buffer 크기를 잘 선택하는 것이 중요하다.)
  • memory map io를 사용하는 것이 단연 가장 빠르다. 심지어 1k 미만의 파일을 읽을 때도 더 빠르다.

일요일, 8월 27, 2006

[Java] Calendar is too slow

java.util.Calendar 객체를 생성하는 데는 상당한 비용이 소요된다.

즉,

      Calendar.getInstance()


라는 메소드를 호출하여 Calendar 객체를 가져오는 데 걸리는 시간이 상당하다.

     // 현재 시간을 가져오는 보통의 방법
     private static Calendar getCalendar1() {
         return Calendar.getInstance();
     }
 
     // 현재 시간은 아니지만 Calendar 객체를 clone을 통해 가져오는 방법
     private static Calendar getCalendar2() {
         return (Calendar) cal.clone();
     }
 
     // Calendar 객체를 clone을 통해 가져와서 현재 시간으로 만드는 방법
     private static Calendar getCalendar3() {
         Calendar c = (Calendar) cal.clone();
         c.setTimeInMillis(System.currentTimeMillis());
         return c;
     }
 
     // 가장 범용적인 GregorianCalendar 객체를 직접 생성하는 방법
     private static Calendar getCalendar4() {
         Calendar c = new GregorianCalendar();
         return c;
     }


위의 메소드를 약 10만회씩 실행한 결과는 다음과 같다.

 // JDK 1.4
 
 it took 391 (ms)
 it took 187 (ms)
 it took 375 (ms)
 it took 328 (ms)
 
 // JDK 1.5
 
 it took 469 (ms)
 it took 266 (ms)
 it took 421 (ms)
 it took 344 (ms)


JDK 1.4와 1.5 모두 그냥 Calendar 객체를 clone을 통해 만드는 것이 가장 빨랐다. Calendar.getInstance()에 비해 거의 두 배가 빠르다.

따라서 단순히 Calendar 객체가 필요하고 여기에 시간을 다시 지정해야 할 것이라면 clone 방법을 사용하는 것이 가장 좋다.

그럼 만약 현재 시간의 Calendar 객체가 필요하다면?

GregorianCalendar를 사용하지 않는 환경에서는 호환성이 문제가 되겠지만 대부분의 지역에서는 직접 new GregorianCalendar()를 사용하는 것이 가장 효율적이다.

그럼 Calendar 객체간의 시간 비교는 어떨까?

다음 테스트는 Calendar 객체와 현재 시간과의 비교이다. 현재 시간이 아닌 경우에는 결과가 많이 다를 수 있다. 그 이유는 Calendar.getInstance()가 new Date()에 비해 훨씬 비싸기 때문이다. 그리고 new Date() 객체 역시 System.currentTimeMillis()에 비해 매우 비싸다.

     // 현재 시간과 비교할 때 new Date()와 비교
     private static boolean compareCalendar1(Calendar cal) {
         return cal.getTime().after(new Date());
     }
 
     // 현재 시간과 비교할 때 Calendar.getInstance()와 비교
     private static boolean compareCalendar2(Calendar cal) {
         return cal.after(Calendar.getInstance());
     }
 
     // 현재 시간과 비교할 때 System.currentTimeMillis()와 비교
     private static boolean compareCalendar3(Calendar cal) {
         return cal.getTimeInMillis() > System.currentTimeMillis();
     }


역시 10만번 수행 결과는 다음과 같다.


 // JDK 1.4
 
 compare1 took 110 (ms)
 compare2 took 328 (ms)
 compare3 took 15 (ms)
 
 // JDK 1.5
 
 compare1 took 78 (ms)
 compare2 took 360 (ms)
 compare3 took 15 (ms)

월요일, 7월 17, 2006

Business Process Management Solution, Tmax BizMaster

비즈니스 마스터라는 이름이 얼마나 어울리는지는 모르겠다.

업무를 프로세스 기반으로 관리한다는 것은 흐름 제어과 그 수행에 대한 분석을 통한 feedback을 포함한다.

C, C++, Java, ... 코딩을 해야지, 프로세스라니...

하는 생각도 많이 한다. 자바 코더로 자바 코딩보다 더 빠르고 편한 게 있을 리가...

어쨌든 BPM은 업무를 자동화해주고 그에 기반하여 업무를 수행하면 그 결과를 관심에 맞춰서 분석하여 알려준다.

세상에 존재하는 다종다양한 업무 패턴을 모아서 워크플로우 패턴 (http://www.workflowpatterns.com) 이라는 사이트를 Wil van der Aalst 라는 네덜란드 교수가 운영한다.

아알스트는 상당히 워크플로우와 BPM에서는 유명한 교수인데, BPM은 BPEL(BPEL4WS 혹은 WS-BPEL)이나 BPML이라는 XML로 정의된 웹 서비스 기반 프로세스로 구축할 수 있다는 분위기가 한창이던 2004년 쯤에 업무 패턴을 실행 못하면 BPM이 아니라는 논지로 반박하면서 워크플로우의 프로세스 엔진 기능의 중요성을 강조했었다.

이 사상에 기반한 BPMN이라는 업무 흐름 표기법이 이맘때쯤 등장했고, BPMN은 지금 OMG에서 표준화 과정을 거치고 있어 업계 표준으로 확고히 자리잡을 전망이다.

티맥스소프트는 필자가 근무하는 회사이다.
그리고 비즈마스터는 필자가 직접 만들고 있는 BPM 제품이다.

"내가 만든 제품이 최고입니다" 이런 말을 하고자 하는 것은 아니다.

하지만, 비즈마스터는 워크플로우 패턴의 사상을 충실히 반영하고 있는 전용 프로세스 엔진에 기반하고 있는 세계 최초의 제품 중 하나이다.
물론, 비즈마스터는 BPEL 엔진도 포함하고 있다.

블럭 모듈 방식의 프로세스 처리를 채택하고 있는 BPEL이나 BPML 같은 프로세스 언어와 단방향 그래프 방식에 기반하고 있는 BPMN 같은 사상은 근원적인 차이를 가지고 있다.
이를 무시하고 이 두 가지 서로 다른 프로세스 처리 방법을 동시에 처리하려는 시도가 몇 있었다.

국내 open source에서도, 또 commercial을 표방하는 제품에서도 사례가 있었지만, 물론 실패하거나 극심한 비효율에 부닥치게 된다.

이것은 프로세스 엔진의 견고성과 효율성을 엄청나게 떨어뜨리는 시도가 된다.
대부분의 국내 BPM 제품들은 유명도에 관계없이 대부분 프로세스 엔진이 너무 단순하고, 엔진이 해야 할 일을 커스터마이즈 내지는 외부 모듈 연계에서 처리하는 경향이 강하다.

그 결과는 아무리 많은 프로젝트를 수행하더라도 엔진의 다재다능함이 늘어나지 않는다는 것이다.

Tmax BizMaster는 그러한 점에서 기본이 다르고 또한 사상이 다르다.
많은 사이트에서 Tmax BizMaster를 새로 채택하고 있다. BPM으로서 EAI로서 여러 가지 측면에서 채택하고 있고, 기반이 되는 탁월한 프로세스 엔진 기술을 갖춘 제품으로서 끊임없이 성장하는 제품이다.

모든 업무에서 항상 효율적으로 적용하는 것은 불가능하다 하더라도 누구보다 많은 분야에 잘 적용할 수 있는 구조를 갖추고 있다.

탁월한 엔진에 기반하여 feature war로 발전하고 있는 그러면서도 더욱 내실에 충실해지는 Tmax BizMaster를 기억해주기를... ^^;;

일요일, 5월 28, 2006

컴파일할 때, 뭘 하나요...

개발자들의 업무 효율과 관련해서 asynchronous event를 제거하는 것의 중요성은 software development team manager에게 매우 중요한 일이다.

개발은 집중을 요구하는데 context switching 혹은 집중으로부터 벗어나는 계기가 되는 일들이 가끔 있다.

메신저류들, 혹은 메일 notification 등등...

그러한 것들 외에도 습관적인 부분들이 존재한다.

컴파일이 시간이 걸릴 경우 혹은 테스트하기 위해 패키징하는 시간이 걸리는 경우, 테스트를 위해 서버 같은 테스트 환경을 부팅 시키는 데 시간이 걸리는 경우 등등 코딩의 연속성을 벗어나게 되는 일들이 있다.

이때 무엇을 하느냐에 따라 개발 효율이 많이 떨어지게 된다.

컴파일하는 동안 잠시 신문을 읽는다든지 웹 서핑을 한다든지 한다면 이 상황에서는 다시 원래의 집중으로 회복하는 데 최소 30분이 소요된다.
이러한 경우에는 컴파일 몇 번 돌리면 하루가 다 가게 된다.

컴파일하는 도중에 계속해서 같은 문맥의 코딩을 할 수 있다면 가장 좋다.
그것이 안된다면, 이러한 때를 위한 가벼운 읽을거리를 마련해두자. 연예정보가 아닌 개발에 필요한 정보를 습득할 수 있는 거리를.. (적어도 뇌의 같은 부위를 사용하는 일을 하도록 해서 context switching이 발생하지 않도록 .. ^^;;)
잠시 호흡을 가다듬는다거나, 뇌를 쉬게 하는 것도 좋다.

불필요하게 뇌를 다른 문맥에서 헤매게 노동시키는 것을 경계하면 된다.
^^;;

토요일, 5월 13, 2006

[Java] serialVersionUID 기본 알고리즘

자바에서 serial version uid를 생성하는 것은 기본적으로 서로 다른 클래스들 간의 구별을 하기 위한 것이다.
동일한 이름을 가진 클래스라 하더라도 메소드나 필드가 다를 경우 서로 다른 것으로 인식하는 것이 기본이기 때문에 Object Serialization을 할 때 import/export 등에서 버전에 따라 종종 불일치 에러가 발생하는 것을 만나게 된다.

클래스가 바뀌었다는 것을 기본 serial version uid 계산 알고리즘을 통해서 검출했기 때문이다.
물론
private static final long serialVersionUID = -6120832682080437368L;
와 같이 클래스에 직접 serial version uid 값을 지정해버리면 기본 uid 계산 알고리즘을 사용하지 않고 이 값을 사용하게 되므로 버전에 따른 불일치 에러는 막을 수 있다.

이렇게 하지 않은 경우 사소한 메소드 시그너처 변경으로도 불일치가 발생하게 된다.

기본 uid 값 계산에 사용되는 정보들은 다음과 같다.

1. 클래스 이름 (fully qualified)
2. 클래스의 접근 제한자 (public, final, abstract, 또 interface 여부)
3. 각 멤버 필드의 시그너처 (이름과 접근 제한자, 타입)
4. 각 멤버 메소드의 시그너처 (이름과 접근 제한자, 각 인자별 정보, 리턴 타입)
4. 각 생성자의 시그너처 (접근 제한자, 각 인자별 정보)
5. static initializer block 존재 유무

이러한 값들을 사용하여 적당한 문자열을 만든 다음 SHA 알고리즘을 사용하여 해시값을 계산한 값이 기본 UID 값이 된다.
이때 필드나 메소드, 생성자의 선언 순서는 바뀌더라도 상관없도록 sort를 한다음 계산을 한다.

여기에서 알 수 있듯이 사소한 변경만으로도 기본 suid 값은 변경되게 마련이다.
따라서 serializable 객체로 객체 통신에 사용되는 클래스들은 가능하면 명시적으로 suid 값을 지정해주는 것이 버전 관리의 문제를 피할 수 있는 가장 깨끗한 방법이다.