본문 바로가기

java

json 역직렬화 성능 측정(feat. jackson, gson, JSONObject) - 2

1. json 역직렬화(feat. jackson)
2. json 역직렬화 성능 측정(feat. jackson, gson, JSONObject)

개요

저번 글에서 jackson이 역직렬화를 어떻게 수행하는지 메서드 콜 스택을 뜯어 소스코드를 직접 살펴보며 알아보았다. 이 때 한 가지 의문점이 있었는데, 정말 성능적인 차이가 있는 것이냐는 문제다.

일반적인 설명으로는 jackson이 streaming api를 이용해서 데이터를 메모리에 모두 올려두지 않고 token('{', ':' 등의 구분자를 토큰으로 명명해둠)로 메모리에만 올려서 해석한 뒤에 flush 시켜서, 성능적으로 더 우위가 있다고 말한다. 하지만 'jackson이 성능적으로 정말 이점이 있는가?'에 대해서는 따져볼 필요가 있다고 판단했다.

jmh

JMH(Java Microbenchmarking Harness)는 java 어플리케이션의 성능을 측정해주는 도구다. 성능을 정확히 측정해주기 위해서는 GC, JIT 컴파일러, 메모리 등의 독립변수를 통제하여 테스트를 할 필요가 있는데, 이를 annotation 기반으로 통제하여 비교적 안정적인 성능 측정을 가능하게 해준다. 가령 warm-up을 한 뒤에 테스트를 해야 정확한 테스트가 가능한데, 이는 jvm의 컴파일 동작방식과 연관이 크다.

Jackson, Gson, JSONObject 성능 측정

매우 간단한 json raw string을 역직렬화하는 메서드를 각 라이브러리를 사용하여 분리한 뒤 benchmarking test를 하였다. key가 2개만 존재하는 아주 단순한 json string을 테스트하였을 시에 다음과 같은 결과를 볼 수 있었다.

초당 operation 수를 측정했을 때 throughput을 scoring 해주는데, JSONObject > gson > jackson 순으로 처리량이 높은 것을 알 수 있다. 간단한 json string이므로 runtime에 라이브러리를 사용하기 위한 객체 초기화 비용 등에서 더 가벼운 라이브러리가 성능이 높을 것을 예상하였는데, 예상대로 처리량이 가벼운 라이브러리 순으로 높았다.

이후에는 json 데이터의 형식을 더 복잡하게 만들고, 데이터의 크기도 약 10MB로 키웠다. JSONObject는 역직렬화만 하고 객체 매핑을 해주지 않기 때문에, 우선은 객체 매핑에 대한 코드가 매우 복잡해졌다(jackson과 gson은 객체 매핑도 지원한다)

10MB 정도로 데이터 사이즈를 키우니 처리량이 유의미하게 차이가 나지는 않았다. 성능상 직접 측정을 해보니 각 라이브러리의 처리량 자체는 오히려 큰 차이가 없다는 것을 발견할 수 있었다. 처리량이나 cpu 사용량에서는 큰 차이를 보이지 않았지만, 메모리 사용량에서 차이가 있었는데 VisualVM으로 측정 시에 다음과 같은 결과를 볼 수 있었다.

Jackson

Gson

JSONObject

jackson과 gson은 큰 차이는 없기는 하였지만 jackson이 다소 메모리 사용을 덜 하는 것을 볼 수 있었고, heap memory에 대한 flush도 더 자주 발생시키는 것을 알 수 있었다. JSONObject는 확실히 메모리 사용량에서 큰 차이를 보였다.

결론

기능 관점에서 객체 매핑 등 고수준의 기능 지원이 필요하다면 jackson과 gson을 사용하는 것이 낫다고 판단했다. JSONObject의 경우 json parsing 후 객체 매핑 또는 각 key에 대한 value 처리가 필요할 시 다른 라이브러리보다 훨씬 많은 코드 처리가 필요했다. 처리량 관점에서는 데이터가 적다면 오히려 JSONObject를 사용하는 것이 더 적합하다고 판단했다. 데이터가 많다면 처리량은 크게 차이가 나지 않지만, 메모리 사용량이 크게 차이가 나므로 jackson과 gson을 사용하는 것을 고려해야 한다.

최종적으로 업무에서는 JSONObject를 활용하기로 결심하였다. 데이터가 많지 않고(현재로써는 과거 데이터를 통해 유추하였을 때 최대 20kb 크기의 json 데이터가 예상되는 상황), 외부 라이브러리에 대한 의존이 org.json 패키지는 이미 지원을 하였기 때문에 라이브러리 도입 비용도 적었다. 추가 library를 사용하기 위해서는 공통 framework 관리 부서를 설득하고 검증해야 하는데, 다른 부서 인적 리소스를 사용할만큼 필요한 상황도 아니었다. json 데이터의 복잡도도 높지 않았고, 결정적으로는 body 데이터가 fixed length 방식을 채택하고 있었기 때문에 객체 매핑이 큰 의미를 갖지 않았다. 결론적으로 간단한 json parsing만 수행하면 되는 상황이었기에 JSONObject를 사용해서 parsing 하는 것으로 최종 결정하였다.