Java와 C의 zip 압축 연산에 대한 퍼포먼스 비교

먼저 C의 zip 압축은 Jean-loup Gailly님이 만들어 공개한 zlib 1.2.2를 사용했습니다. C/C++에서 데이터의 압축에서 사용하는 압축 라이브러리는 흔히 이 zlib를 사용합니다.  그리고 Java에서 압축은 기본적으로 제공하는 java.util.zip.Deflater 클래스를 사용했습니다. 테스트를 한 이유는 Java가 C/C++에 비해서 얼마나 느릴까… 하는 기대였습니다. =_=;

Java와 C/C++ 모두 사용한 압축 데이터는 0.2메가 정도되는 jpg 파일로 했습니다. 그리고 Java와 C/C++ 모두 결과는 압축 레벨을 3으로 했을때 동일한 결과와 크기였으며 원본 크기에 비해 75% 정도의 압축되었습니다. 결과는 다음 같습니다. 첫번째 이미지는 Java의 결과이고.. 두번째는 C/C++의 결과입니다.

사용자 삽입 이미지
와우!! Java가 C/C++에 비해 상당히 느릴것으로 기대했는데… 그렇지 않았습니다. C/C++와 성능은 거의 비슷한 것으로 생각됩니다. 두 경우 모두 최악의 경우 0.016초정도 소요됩니다. 다만…. Java의 경우 가끔씩 튀는 부분이 있었는데.. 0.031초 정도 소요되는 부분이 가끔 나옵니다. 아마도 Java의 gc기능 때문이 아닌가… 가볍게 짐작해봅니다. 하지만 이런 부분은 제외하면 정말 C/C++과 같은 Native 컴파일러 못지 않은 성능이라고 판단됩니다.

아래는 Java에서 퍼포먼스 테스트로 사용했던 코드입니다.

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import java.io.*;

public class TestMain {
    public static void main(String[] args) 
        throws IOException, DataFormatException
    {
        FileInputStream fis = new FileInputStream("d:/a.jpg");
        ByteArrayOutputStream baos = new ByteArrayOutputStream(fis.available());
        byte [] buffer = new byte[512];
        int cntRead;
        while((cntRead=fis.read(buffer)) != -1) {
            baos.write(buffer, 0, cntRead);
        }

        byte[] compressedBytes = null;
        for(int i=0; i<20; i++) { // 20 times repeat..
            System.out.println("Performance Test Start...");
            long stime = System.currentTimeMillis();
 
            compressedBytes = Compress(baos.toByteArray());
   
            long etime = System.currentTimeMillis();
            System.out.println("Performance Test Result : " 
                + (etime-stime)+" MS.");
        }   
 
         FileOutputStream fos = new FileOutputStream("d:/a_java.jpg.zip");
         ByteArrayInputStream bais = new ByteArrayInputStream(compressedBytes);
         while((cntRead=bais.read(buffer)) != -1) {
             fos.write(buffer, 0, cntRead);
         }
    }

    private static byte[] Compress(byte[] bytesToCompress) throws IOException
    {
        Deflater compressor = new Deflater(3);
        compressor.setInput(bytesToCompress);
        compressor.finish();
 
        ByteArrayOutputStream bos = 
            new ByteArrayOutputStream(bytesToCompress.length);
 
        byte[] buf = new byte[bytesToCompress.length + 100];
        while (!compressor.finished())
        {
            bos.write(buf, 0, compressor.deflate(buf));
        }

        bos.close();
 
        return bos.toByteArray();
    }
}

대략 살펴보시면 위의 코드에는 IO에 대한 Buffer 기능과 같은.. 여전히 최적화의 여지가 남아 있습니다. 더 이상 제가 갖고 있는 “자바는 느리다”라는 선입견이 상당 부분 깨진 느낌입니다.

텍스트 효과 단위기능 테스트

구글맵이나 네이버지도, 다음지도 등등 모든 포털 지도사이트에서 제공하는 지도 서비스는 지도 이미지를 256×256 픽셀 크기로 잘려진 타일이미지 형태로 서비스되고 있습니다. 자사만의 지도 타일이미지 렌더링 프로그램을 직접 개발한 곳도 있을테고…. 구글처럼 오픈소스 라이브러리를 이용해 구축한 곳도 있을테고….

현재  RIA 기반의 지도 솔루션 개발에 필요한 지도 타일 이미지 렌더러를 개발하고 있습니다. 오픈소스를 활용하여 개발할까… 생각도 해보았지만… 다양한 사용자가 원하는 자신만의 지도 스타일의 충족이라는 틈새 시장을 놓고 볼때… 오픈소스의 획일화된 지도 이미지 스타일은 제 기획 의도와 맞지 않다고 판단하여 직접 개발하기로 결정하게 되었습니다.

사용자 삽입 이미지
위의 결과 이미지는 현재 개발중인 지도 타일이미지 렌더러에서 텍스트 표현을 위해 고민해본 텍스트 효과입니다. 실제 종이 지도를 살펴보고 글자에 적용된 효과를 살펴보면서 위의 3가지 효과정도면 될듯하여 하나 하나 구현해 보았습니다.

추후 타일 이미지 렌더러… 개발이 완료되면 위의 텍스트 효과가 적용된 지도를 통해 좀더 분명한 결과를 알릴 수 있겠지만.. 지금 단계에서 현재 제 스스로가 무엇을 하고 있는지… 어느 방향으로 가고 있는지에 대해 한 조각이라도 구체화하기 위한 과정으로써의 글을 올려봅니다.

[C++] XML 파서, CMarkup

XML 데이터를 쓰고 읽기 위해서 인터넷을 검색하던 차에 속도를 강점으로 내세우면서 STL 만을 사용하여 플랫폼 이식에도 뛰어난 오픈소스를 찾았는데요. 바로 CMarkup 입니다. 다운로드 사이트는 http://www.firstobject.com/ 이구요. 사용해 보니 XML의 charset도 지원하여 더욱 믿음이 가는 오픈소스였습니다. XML을 처리할 일이 있다면 한번 사용해 보시기 바랍니다.

추후 이 오픈소스를 다시 사용할 때를 대비하여 사용 방법을 정리해 정리차원에서 올려봅니다.

컴파일 시 주의할 사항은… CMarkup은 MFC의 CString와 STL의 string에 대한 문자열 타입을 사용하며 기본적으로 CString을 사용합니다. 플랫폼에 독립적인 구성을 위해서 STL을 사용하는것이 좋기 때문에 전처리에서 MARKUP_STL를 정의해줘야 합니다. 이 전처리 정의는 프로젝트의 속성 페이지에서 해줌으로써 전역적으로 적용되도록 해야 합니다. 아래의 코드는 XML을 쓰는 예제 코드입니다. 윈도우즈 계열의 개발툴인 VS2008로 작성했습니다.

#include "stdafx.h"
#include "../Markup.h"

int _tmain(int argc, _TCHAR* argv[])
{
    CMarkup xml;

    xml.AddElem( "ORDER" );
    xml.AddChildElem( "ITEM" );
    xml.IntoElem();
    xml.AddAttrib("type", "A");
    xml.AddChildElem( "SN", "132487A-J" );
    xml.AddChildElem( "NAME", "crank casing" );
    xml.AddChildElem( "QTY", "1" );
    xml.OutOfElem();

    xml.AddChildElem( "ITEM" );
    xml.IntoElem();
    xml.AddAttrib("type", "C");
    xml.AddChildElem( "SN", "434417F-Y" );
    xml.AddChildElem( "NAME", "kully casing" );
    xml.AddChildElem( "QTY", "2" );
    xml.OutOfElem();

    std::string csXML = xml.GetDoc();

    printf("%s", csXML.c_str());

    return 0;
}

결과는 다음과 같습니다.

사용자 삽입 이미지
AddElement를 통해 엘리먼트를 만들고, 해당 엘리먼트의 자식을 추가하기 위해 AddChildElem을 사용하거나 먼저 IntoElem을 호출한 후 다시 AddElement를 사용합니다.

이제 반대로 위와 같은 XML 데이터를 읽어 보도록 하겠습니다. 먼저 위의 결과를 파일로 저장해 놓고 그 파일을 읽어 데이터를 추출하는 예제를 들어 보겠습니다. 예를 들어… 아이템(ITEM)의 이름(NAME)과 수량(QTY)을 읽어 보도록 하겠습니다. 위의 XML 문자열의 경우 아이템의 개수는 총2개이므로 2개가 검색될 것입니다. 다음이 이 예제와 부합되는 코드입니다.

CMarkup xml;
xml.Load("d:/data.xml");

while ( xml.FindChildElem("ITEM") )
{
    xml.IntoElem();

    xml.FindChildElem( "NAME" );
    std::string csSN = xml.GetChildData();
    xml.FindChildElem( "QTY" );
    int nQty = atoi(xml.GetChildData().c_str());

    xml.OutOfElem();

    printf("%s, %d\n", csSN.c_str(), nQty);
}

먼저 XML 데이터를 가지고 있는 파일을 Load 매서드를 이용해 읽습니다. 다시 반복문을 사용하여 루트 엘리먼트의 자식 ITEM 엘리먼트를 검색하기 위해 FindChildElem을 사용합니다. 그러면 해당되는 자식 엘리먼트가 추출됩니다. 해당되는 자식 엘리먼트의 자식 엘리먼트를 읽기 위해 IntoElem() 함수를 사용한 뒤에 원하는 엘리먼트(NAME, QTY)를 검색하기 위해 FindChildElem을 호출하고 실제로 값을 읽기 위해서 GetChildData 매서드를 호출합니다. 다 읽은 후 OutOfElem()을 호출합니다. 결과는 아래와 같습니다.

사용자 삽입 이미지

타원의 방정식

이 얼마나 오랜만에 써보는 포스팅인지 모르겠습니다. 요즘 이러 저런 일로 바쁘다보니 블로그 관리에 매우 소홀했습니다. 이제 부터라도 짬짬히 시간을 내어.. 일상 업무에서 찾은 내용을 올리도록 노력 해야겠습니다. 해서… 알고보면 매우 간단한 내용이지만 포스팅 하나 올려봅니다.

오늘, 열심히 코드를 작성하던 중에.. 2차원에서 타원을 구성하는 좌표를 뽑아 낼 필요가 있었습니다. Needs는 하나의 타원과 또 다른 하나의 폴리곤을 하나의 도형으로 합(Union)하는 연산이 필요할듯 한데… 타원에 대한 정보는 단순히 중심점과 장반경 그리고 단반경만을 가지고 있음으로 폴리곤에 바로 합할 수 없는지라.. 일단 타원을 구성하는 정점을 이용하여 폴리곤으로 만들고.. 폴리곤과 폴리곤의 합 연산을 통해 원하는 결과를 얻고자 함이였습니다.

간단히 타원의 공식은 인터넷(http://en.wikipedia.org/wiki/Ellipse)을 통해 아래처럼 얻었습니다. 물론 프로그래밍에서 쉽게 사용할 수 있는 매개변수방정식으로 말입니다.

사용자 삽입 이미지
위의 식에서 Xc와 Yc는 타원의 중심입니다. 그리고 A와 B는 각각 X축과 Y축에 대한 타원의 반경이며 각각을 장축과 단축이라고 하겠습니다. 그리고 t는 0도에서 360도까지의 범위입니다. 물론 0도와 360도는 동일하므로 둘 중 하나는 포함되지 않아야 합니다.  마지막으로 ∅는 장축과 X축이 이루는 각도 입니다. 즉 ∅를 통해 기울어진 타원을 구성하는 좌표를 정의할 수가 있습니다. ∅에 대한 이해를 돕기 위해 아래 그림을 참고 하시기 바랍니다.

사용자 삽입 이미지

[GIS] 개발 맵 엔진의 Slope, Aspect, Hillshade

지형이라는 주제로 제작된 라스터(Raster)의 셀(Cell)에 기반한 분석 중 경사(Slope)와 향(Aspect) 분석은 중요한 자리를 차지하며, 이 경사와 향은 지형에 대한 분석 및 응용의 파라메터로 사용됩니다. 이 글은 개발 중인 지도 엔진의 확장 기능의 하나로써 경사와 향을 분석하는 것과 이 경사와 향을 분석하는 기능을 응용하여 다시 지형의 음영도(Hillshade)를 만드는 확장 기능에 대한 소개입니다. 여기서 확장 기능이란 지도 엔진과는 별개의 파일 단위로써 엔진에 플러그인하여 별도의 스레드를 통해 실행시킬 수 있는 외부의 확장 실행 단위 모듈입니다.

경사와 향 값을 분석하기 위해서는 분석 대상이 되는 지역에 대한 DEM 데이터가 필요합니다. 이 DEM의 라스터 데이터로써 아래와 같은 데이터를 사용하였습니다.

사용자 삽입 이미지
이 데이터는 김한국님이 운영하는 비지니스 GIS 커뮤니티(www.biz-gis.com)에서 내려 받은 데이터입니다. 그리고 위의 이미지를 포함해서 앞으로 나오는 지도 화면은 모두 오픈메이트(www.openmate.co.kr)의 개발한 맵 엔진으로 생성한 화면입니다. 위의 데이터를 통해 경사값과 향을 계산하기 전에 간단히 경사와 향이 무엇인지에 대해 언급하겠습니다.

경사(Slope)란 어떤 지점의 지반이 수평을 기준으로 몇도 정도 기울어져 있는가를 말하는 것으로 다음 그림을 통해 쉽게 이해할 수 있을 것입니다.

사용자 삽입 이미지
바로 저 세타 각도가 경사 기울기에 대한 각도로써 각이 클 수록 지반의 경사가 급하고 각이 0이면 평편한 지반임을 나타냅니다.

향(Aspect)이란 지반의 경사면이 어디를 향하고 있는지에 대한 것입니다. 만약 지반의 경사면이 북쪽을 향하고 있다면 0도, 동쪽은 90도, 남쪽은 180도 그리고 서쪽은 270도가 됩니다. 완전히 평편할 경우 GIS 시스템마다 다른 값을 주게 되는데, 여기서는 Null 값을 줍니다. (사실 Null은 값이 없다는 의미이므로, 좀더 정화하게 한다면 0~360이 아닌 다른 값을 주어야 옳습니다. 예를들어서 -1과 같은 값이 적당하겠습니다)

실제로 구현된 경사와 향에 대한 실행 결과는 아래와 같습니다. 각각 경사와 향에 대한 이미지입니다.

사용자 삽입 이미지
가만이 보면 향의 분석 결과가 불완전하기는 하지만 음영기복도(Hillshade)와 비슷해 보입니다. 여기서 Hillshade는 태양광의 위치를 정해주면 태양광의 영향으로 지형 데이터를 입체감 있게 표현해 주는 방법입니다. 아래는 이러한 내용으로 개발한 맵 엔진에 구현된 Hillshade의 결과입니다.

사용자 삽입 이미지
태양의 위치는 북동쪽(270)이고 고도는 45도로 정했습니다. 위의 Hillshade의 결과는 비록 입체적으로 보이기는 하지만 마치 석고를 발라 놓은 듯이 매우 딱딱해 보입니다. 이를 좀더 예술적(?)으로 표현하기 위해 이 글의 가장 처음 보여드렸던 지형 데이터의 원본 이미지와 위의 Hillshade를 합성하게 되는데, 좀더 멋진 음영기복도를 위한 합성 방법은 Hillshade을 약간 투명하게 하고 Luminosity 방식으로 합성해야 하나, 개발한 맵 엔진에서는 Luminosity 방식의 합성을 지원하지 않아 그냥 투명도만 주어 새롭게 음영기복도를 만들어 보았습니다. 그 결과는 아래와 같습니다.

사용자 삽입 이미지
봄 계절에 맞게 색상을 넣어 보았습니다. 좀더 색상을 다체롭게 넣는다면 더 멋진 음영기복도 만들어 질 것으로 판단되며, 앞서 언급한 Luminosity 합성을 사용하면 보다 분명한 음영기복도가 만들어 질 것 입니다. 이상으로 개발한 맵 엔진에서 확장기능으로 개발한 경사, 향 그리고 Hillshade에 대한 소개를 마칩니다.