비교할 문제

Stream과 반복문 속도 비교에서 확인할 질문은 JVM에서 loop, Stream과 collection 메서드의 실제 비용을 어떻게 비교할까입니다. 같은 결과를 반환하고 dead-code elimination을 막습니다. boxing, 중간 collection, exception path와 parallel 여부를 명시합니다. 결과값, 순서, 오류 처리와 부수 효과가 다르면 같은 성능 비교가 아닙니다. 먼저 두 후보가 같은 일을 하는지 작은 입력으로 확인합니다.

원인 분석과 재현

Stream과 loop는 같은 순서·short-circuit·allocation 결과를 만들 때 JMH에서 비교해야 하며 parallel stream은 별도 실행 계약입니다.

원소 100개·10만 개, primitive·boxed value, 짧은 작업·비싼 작업을 나누고 JDK version, GC, heap과 CPU를 고정합니다. runtime version, 운영체제, CPU와 memory 제한도 결과와 함께 기록합니다.

비교 질문은 JVM에서 loop, Stream과 collection 메서드의 실제 비용을 어떻게 비교할까입니다.

후보 유리한 조건 함께 치르는 비용
명령형 loop primitive 단순 순회와 세밀한 조기 종료 복잡한 상태 변경은 검증과 병렬화가 어려움
sequential Stream filter-map-reduce 의도가 선명한 pipeline boxing·lambda·pipeline overhead가 작은 작업에서 두드러질 수 있음
parallel Stream 충분히 크고 분할 가능한 CPU 작업 common pool 경합, 결합 비용과 순서 보장 비용
@State(Scope.Thread)
public class LoopBenchmark {
  private int[] values;
  @Setup public void setup() { values = IntStream.range(0, 100_000).toArray(); }
  @Benchmark public long loop() { long sum = 0; for (int v : values) sum += v; return sum; }
  @Benchmark public long stream() { return Arrays.stream(values).asLongStream().sum(); }
}
// 예: -wi 5 -i 10 -f 3. warm-up, measurement, fork 결과를 모두 보관합니다.

해결 방안과 판정

warm-up과 측정 구간을 나누고 여러 번 반복합니다. 서비스 지연은 median과 p95를, 처리량 비교는 단위 시간당 완료 수와 오류율을 함께 기록합니다. CPU time, allocation, RSS·heap, GC, I/O 중 이 작업에 직접 관련된 지표만 선택합니다. JIT가 안정되기 전 한 번 잰 nanoTime 결과는 결론이 아닙니다. JMH fork와 warm-up을 사용하고 실제 service에서는 allocation rate와 p99도 다시 확인합니다.

차이가 반복 실행의 흔들림보다 작다면 더 단순하고 읽기 쉬운 구현을 선택합니다. 차이가 충분히 크더라도 실제 요청 전체의 지연과 자원 사용이 개선되는지 확인한 뒤 적용합니다.

공식 문서와 적용 범위

문서의 기본값과 동작은 version에 따라 달라질 수 있습니다. 현재 JDK/JVM version, 실행 옵션, collector, runtime 환경과 실제 memory 사용 패턴에서 다시 확인합니다.