From f84cab9bd9fa75e24722efcafe08abcc9d0b5e7d Mon Sep 17 00:00:00 2001 From: donoghuc Date: Fri, 24 Jul 2026 09:37:06 -0700 Subject: [PATCH] Avoid per-row Enumerator#next in CSV#each External iteration via Enumerator#next uses a Fiber, cheap on CRuby but Fiber-per-row (native thread) on JRuby, so CSV.parse_line and CSV#each are ~10x slower there. Drive the parser with a block on fresh iteration; keep Enumerator#next only when #shift/#peek already started pull-based iteration, preserving shared consumption (GH-260). Closes https://github.com/ruby/csv/issues/361 --- lib/csv.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/csv.rb b/lib/csv.rb index 3b7fee8..9c10c39 100644 --- a/lib/csv.rb +++ b/lib/csv.rb @@ -2688,11 +2688,17 @@ def header_convert(name = nil, &converter) # end def each(&block) return to_enum(__method__) unless block_given? - begin - while true - yield(parser_enumerator.next) + # Avoid per-row Enumerator#next (Fiber-backed, slow on JRuby) unless a + # #shift/#peek has already started pull-based iteration. + if @parser_enumerator.nil? + parser.parse(&block) + else + begin + while true + yield(@parser_enumerator.next) + end + rescue StopIteration end - rescue StopIteration end end