본문 바로가기
OpenSource

오픈소스 - Naver Arcus Client - MapDelete, MapGet JMH 벤치마킹

by 준형코딩 2024. 10. 10.

오늘은 Naver Arcus Client 오픈소스 프로젝트에 대한 성능 분석 결과를 공유하고자 합니다. 특히 문자열 연결(String Concatenation) 부분에 초점을 맞추어 분석을 진행했습니다.

1. 배경

Naver에 대한 관심으로 시작된 이 분석은 오픈소스 프로젝트를 탐색하는 과정에서 시작되었습니다. 코드를 살펴보던 중, MapDelete와 MapGet 클래스의 getSpaceSeparatedMkeys() 메소드에서 복잡한 for 루프와 StringBuilder를 사용하는 것이 눈에 띄었습니다. 왜 이렇게 복잡한 방식을 사용했는지 궁금해졌고, 이를 간단한 String.join()으로 리팩토링해보기로 결정했습니다.

2. 분석 대상

분석 대상은 MapDelete와 MapGet 클래스의 getSpaceSeparatedMkeys() 메소드였습니다. 이 메소드는 키-값 쌍을 문자열로 변환하는 역할을 합니다. 

3. 원본 코드와 리팩토링된 코드

원본 코드 (StringBuilder 사용)

  public String getSpaceSeparatedMkeys() {
    if (spaceSeparatedKeys != null) {
      return spaceSeparatedKeys;
    }

    StringBuilder sb = new StringBuilder();
    int numkeys = mkeyList.size();
    for (int i = 0; i < numkeys; i++) {
      sb.append(mkeyList.get(i));
      if ((i + 1) < numkeys) {
        sb.append(keySeparator);
      }
    }
    spaceSeparatedKeys = sb.toString();
    return spaceSeparatedKeys;
  }

리팩토링된 코드 (String.join() 사용)

public String refactoredMethod() {

    if (spaceSeparatedKeys != null) {
        return spaceSeparatedKeys;
    }
    
    spaceSeparatedKeys = String.join(keySeparator, mkeyList);
    
    return spaceSeparatedKeys;    
}

리팩토링된 코드 (StringJoiner 사용)

public String stringJoinerMethod(List<String> testList, String separator) {

    StringJoiner joiner = new StringJoiner(separator);
    
    for (String element : testList) {
        joiner.add(element);
    }
    
    return joiner.toString();
}

 

4. 성능 테스트

성능 테스트를 위해 JMH(Java Microbenchmark Harness)를 사용했습니다. Naver Arcus Client의 실제 사용 범위에 맞춰 4,000, 5,000, 10,000, 50,000개의 요소에 대해 벤치마크를 실행했습니다.

 

5. 테스트 결과

아래는 각 방법별 평균 실행 시간(나노초)입니다

6. 분석

  1. 모든 데이터셋 크기에서 StringBuilder가 가장 좋은 성능을 보였습니다.
  2. String.join()은 StringBuilder에 근접한 성능을 보였으며, 특히 작은 크기의 데이터셋에서는 거의 비슷한 성능을 나타냈습니다.
  3. StringJoiner는 예상과 달리 세 방법 중 가장 낮은 성능을 보였습니다. 특히 데이터셋의 크기가 커질수록 다른 두 방법과의 성능 차이가 더 벌어졌습니다. (StringJoiner는 for문으로 연결 시 속도가 저하 된다고 합니다.)
  4. 데이터셋 크기가 증가할수록 세 방법 간의 성능 차이가 더욱 두드러졌습니다.

7. 결론

  1. Naver Arcus Client의 사용 범위(4,000-50,000개)에서는 StringBuilder를 사용하는 것이 성능 면에서 가장 유리합니다.
  2. String.join()은 StringBuilder와 비교적 근접한 성능을 보이면서도 코드가 간결하므로, 성능이 약간 덜 중요한 경우에는 사용을 고려해볼 수 있습니다.
  3. 예상과 달리 StringJoiner의 성능이 가장 낮았으므로, 대규모 데이터셋에서는 사용을 피하는 것이 좋습니다.
  4. Naver Arcus Client의 경우, 다음과 같은 접근을 권장합니다:
    • 성능이 매우 중요한 부분: StringBuilder 사용
    • 코드 가독성이 더 중요한 부분: String.join() 사용 (성능 손실이 크지 않은 경우)

코드의 가독성만을 고려해 접근했던 분석이었지만 실제 성능 테스트를 통해 새로운 통찰을 얻을 수 있었습니다. StringBuilder가 대규모 데이터 셋에서 압도적으로 뛰어난 성능을 보인다는 사실은, 표면적으로 코드의 복잡성을 보고 판단하는 것을 조심해야 한다는 것과 어떤 기술을 선택할 때 성능 테스트의 중요성을 일깨워 주었습니다. 이번 경험을 통해 오픈소스와 프로그래밍에 대한 이해가 더욱 깊어졌으며, 코드 분석에 있어 다각적 접근의 필요성을 체감할 수 있었습니다.