다중 통화 지출 리포트

FxDart 승

요구사항

여행 가계부(데이터는 코드에 있음)에는 EUR, GBP, JPY, USD 금액이 섞여 있습니다. 코드에 있는 고정 환율로 모두 USD로 환산한 뒤 다음 항목을 리포트하세요: 지출액순으로 정렬된 카테고리별 총액, 사용된 통화 목록, 가장 큰 단일 지출(원래 금액 포함), 그리고 총합계. 두 버전 모두 예상 출력 아래에 표시된 리포트를 출력해야 합니다.

예상 출력
Trip expenses in USD (currencies: EUR, GBP, JPY, USD)
  Lodging  $240.55
  Travel   $164.82
  Food     $37.85
  Transit  $22.50
Largest single expense: Travel $130.80 (120.00 EUR)
Total: $465.72

나란히 보기

네이티브 Dart

FxDart

차이가 나는 이유

먼저 정규화하는 것 — 각 거래를 (tx, usd) 쌍으로 map하는 것 — 은 이후의 모든 질문이 하나의 리스트 위에서 돌아가게 해 줍니다: 내역에는 foldBy + sortBy, 통화 목록에는 uniq, 요약 줄에는 maxBysumBy. 리포트의 각 줄은 자신의 집계 방식을 이름으로 드러내는 짧은 파이프라인 하나입니다. 네이티브 버전은 똑같은 동작을 하지만 그 어휘가 없습니다: 카테고리별 합계는 직접 굴린 맵 누적기이고, 정렬에는 비교자를 일일이 써 줘야 하며, 최댓값은 reduce 비교자, 통화 목록에는 toSet().toList()..sort() 조합이 필요합니다. 어려운 부분은 없습니다 — 다만 코드량이 더 많고, 그 코드가 의미를 덜 말해 줄 뿐입니다.

집계에 groupBy + sumBy가 아니라 foldBy를 쓴 것은 의도적이며, 잠깐 짚고 갈 만합니다. 여기서 원하는 답은 카테고리마다 숫자 하나입니다. 그러니 먼저 그룹으로 묶으면 카테고리별로 모든 거래의 List를 만들었다가 곧바로 접어서 없애게 됩니다 — 답은 카테고리 개수에 비례하는데, 할당은 입력에 비례하는 셈이죠. foldBy는 결과 맵에 곧장 누적하는데, 이는 옆에 있는 네이티브 반복문이 하는 일과 정확히 같습니다. 백만 행짜리 원장에서는 이 선택 하나가 양쪽 모두에서 약 2.5배의 차이를 만듭니다. 빠른 파이프라인 작성하기를 보세요. 정말로 구성원이 필요할 때만 groupBy를 쓰세요.

벤치마크

Apple M1 Max, RAM 32 GB · Dart 3.12.2 (AOT 컴파일) · 2026-08-24

N = 100

시간 무승부

네이티브 Dart 14 µs
FxDart 11 µs

최대 메모리 무승부

네이티브 Dart 17.0 MB
FxDart 16.5 MB

N = 10,000

시간 무승부

네이티브 Dart 784 µs
FxDart 719 µs

최대 메모리 무승부

네이티브 Dart 23.5 MB
FxDart 22.9 MB

N = 1,000,000

시간 FxDart 승

네이티브 Dart 123.3 ms
FxDart 104.4 ms

최대 메모리 네이티브 승

네이티브 Dart 201.8 MB
FxDart 220.0 MB

막대는 사이드별로 새 프로세스에서 반복 측정한 중앙값입니다(작은 N은 타이머 해상도를 위해 배치 처리). 두 사이드가 서로 5% 이내이거나 — 사람이 지각할 수 없는 차이인 0.6ms 이내이면 — 무승부로 칩니다. 상대 차이가 근소한 경우는 최대 5회까지 다시 측정합니다. 앱에서는 어느 막대가 짧든 몇 밀리초 이하의 차이는 사용자에게 보이지 않습니다. 메모리는 프로세스 최대 RSS입니다. Dart VM과 데이터셋은 양쪽이 동일하므로, 두 막대의 차이가 곧 파이프라인 자체가 붙들고 있는 양입니다.