2013년 4월 20일 토요일

[BigScatterChart] 대용량 스캐터 차트 개발 공유


1. 요구조건
 - 50만개 이상에도 끄떡없는 대용량 스케터 차트
 - 마우스 드래그시 해당 영역의 스캐터를 반환해야 함
 - 스캐터 점의 타입이 여러개가 될 수 있음
 - 특정 주기마다 차트가 업데이트 될 수 있음

2. 기존 스캐터 차트
 - d3.js visualization을 이용하여 개발되었음
 - SVG를 이용하고, d3.js에서 기본적으로 제공하는 마우스 이벤트나 xy축 자동 계산, formating등등이 용이함
 - 상단의 스캐터 차트는 약 8만개의 점을 그리고 있음
 - 8만개를 한번에 로딩해서 불러오지 않고, 5천개씩 끊어서 로딩 후 그림
 - 다 그린 후 약 8만개의 DOM Elements가 생성됨
 - 메모리가 여유가 없을 경우, 차트를 그리는 이외의 작업을 할 수가 없음
 - 마우스 드래그 검색시, 선택 영역이 커질 수록 반응 속도가 느림

3. 리서치한 다른 스캐터 차트
  - SVG를 이용하며, 기능이 d3.js에 비해 많이 부족함, 이럴바엔 d3.js를 사용하는게 나음

 3.2 NVD3.js Scatter Chart (http://nvd3.org/ghpages/scatter.html)
  - d3.js를 이용하여 개발되었음
  - 마우스 오버시, 가이드 라인기능
  - Magnify 기능을 이용하여 해당 부분을 마우스를 이용하여 돋보기 할 수 있음
  - UI에서 드래그선택 기능을 제외하고 필요한 기능은 이미 개발 되어 있음
  - 하지만, 50만개 점을 그릴때 여전히 SVG는 50만개의 DOM Elements를 만들어서 느림
  - BSD License

 3.3 canvasXpress Scatter Chart (http://canvasxpress.org/scatter2d.html)
  - Canvas를 이용함, DOM Element가 거의 필요 없음
  - 마우스 영역 선택시, 해당 부분 확대 기능
  - 스캐터 점에 따라 회귀선 또는 분포선이 자동으로 그려짐
  - 3D 스케터도 지원함 (http://canvasxpress.org/scatter3d.html
  - LGPL 3.0 License.

 3.4 moochart (http://moochart.coneri.se/)
  - Canvas를 이용함
  - 너무 심플하여 기능 개발이 많이 필요함
  - MooTools javascript framework에 종속적임
  - MIT License

 3.5 결론
  - 이외에도 여러가지 차트가 많이 있다. svg를 이용하여 개발할 경우 DOM을 이용하여 각종 이벤트를 제어 가능하기에 사용자를 만족시킬 수 있는 인터렉션을 쉽게 개발 할 수 있다. 하지만 Bubble(스캐터 점)이 늘어 날 수록 DOM의 갯수가 늘어나 브라우저가 느려진다.
그래서 많은 양의 데이터는 2D 캔버스를 이용하여 그리는 것이 성능에는 좋다. 왜냐하면 필요한 DOM만 가지고 있기 때문이다. 리서치 한 결과 canvasXpress의 Scatter Chart가 Mash up 개발을 하기에 적합하였으나, 라이선스문제와 불필요한 기능들로 인한 성능 저하 예상으로 포기하였다. 대안으로 moochart를 이용하여 필요한 기능들만 추가 개발하려고 하였으나, jQuery가 아닌 MooTools을 이용하여 개발되었기에 moochart의 컨셉만을 가지고 새로 개발하기로 한다.

4. BigScatterChart

 4.1 주요 피처
  - 50만개 이상의 점을 찍더라도 느려지지 않음
  - 상단 Type 클릭시 보기, 감추기
  - 상단 Type 드래그&드랍 시, 스캐터 차트 Layer 순서 변경
  - 차트 영역에서 드래그 & 드랍시 셀렉트박스로 해당 부분 선택
  - 드래그 셀렉팅 후 스캐터 차트 영역에 맞게 맞추기
  - 드래그 설렉팅 후 해당 영역 검색
  - 특정 주기로 차트 업데이트시, 드래그 셀렉팅 영역도 같이 이동
  - 특정 주기로 차트 업데이트시, 약0.004s 시간 소요(50만개 전체 업데이트시, 약4초 소요)
  - 각종 커스텀 이벤트 제공
 4.2 성능개선
  - SVG에서 Canvas로 변경하여, 성능저하 요인인 DOM Elements를 최소화
  - 특정 주기 업데이트시, 한번 Canvas에 그린 스캐터 버블은 다시 그리지 않고, 해당 영역을 이동 후 추가분만 그리기
  - 가이드라인 Canvas와 스캐터 Canvas를 분리하여, 불필요한 작업을 줄이고, Type별로 Canvas를 또 분리하여 Type별로 제어가 가능함
  - 스캐터 테이터를 1차원 배열에 저장하지 않고, 2차원 배열에 저장함으로써, array[들어온 순서][데이터] 로 분리하고, arrayInfo[들어온 순서] 에서 최소, 최대값등등의 정보를 캐싱 함
  - 드래그 셀렉팅 검색시, x, y, width, height를 x-from, x-to, y-from, y-to로 파싱하고, 스캐터 데이터에서 보다 효율적으로 검색하기 위해 arrayInfo에 캐싱된 정보이용하여 1차 판별 후,  2차 검색을 실시하여 결과를 리턴
  - 특정 주기로 업데이트시, 사라지는 영역의 데이터 버림
  - 상단 Type 드래그 소팅시 Canvas Layer의 순서를 변경할 때, jQuery의 before, after 메소드를 이용하지 않고, z-index 값으로만 조정. 이유 : jQuery before, after는 해당 DOM Element를 Clone 하여 복사하고, 기존 값은 지우는데, 이때 Canvas의 Context는 복제하지 않음. Canvas용 before, after를 만들 수도 있지만, z-index 변경이 더 심플하고, 강력함.

5. 성능 비교
비교
기존(D3.js)
8만개 실데이터
신규(BigScatterChart)
50만개 랜덤데이터
렌더링 속도
약 33초
약 6초
렌더링 메모리
150 ~ 250M
50 ~ 80M
드래그 셀렉팅 속도
3초이상
0.2초 이내
드래그 셀렉팅 메모리
80M 이내
20M 이내
(위 성능 비교 자료는 iMac 10.8.3, 2.7Ghz, 16G, 크롬 26.0 버전에서 '활성 상태 보기' 유틸리티를 이용하여 이루어짐)

6. Feedback
 - 이밖에 BigScatterChart 성능 개선 관련하여 좋은 아이디어를 가지고 계시면 공유 부탁드립니다.

감사합니다.