Repository navigation
Description
Feature or enhancement
With newline='', the mode the csv documentation recommends, TextIOWrapper.readline() and iteration search for \r or \n one character at a time (in the universal newlines branch of _PyIO_find_line_ending()). With newline=None and newline='\n', they use memchr(). As a result, the same file reads much more slowly with newline=''. With a PGO+LTO build of main on Linux:
$ ./python -m timeit "for line in open('data.csv', newline=''): pass"
10 loops, best of 5: 21.9 msec per loop
$ ./python -m timeit "for line in open('data.csv', newline='\n'): pass"
20 loops, best of 5: 12.3 msec per loop
(data.csv is 24 MB: 300,000 rows of ASCII CSV.)
The newline decoder has two similar loops:
- With
newline='', it scans each chunk for\rand\nto keep thenewlinesattribute up to date, and stops only after seeing all three kinds of line ending. For a file with only\r\nendings, which is whatcsv.writerwrites, it scans every chunk. - With the default
newline=None, it translates\r\nand\rto\none character at a time in every chunk that contains\r.
For 1-byte strings (no character above U+00FF), all three loops can skip ahead one size_t word at a time: a cheap test skips words with no byte below 0x0E, an exact test finds \r and \n in the rest, and ctz() gives the position, as in find_first_nonascii() in unicodeobject.c. With this change, the first command above takes 13.8 msec per loop.
In interleaved runs on 24 MB files with PGO builds (gcc and MSVC):
- Iterating over lines with
newline=''is 1.35x to 1.6x faster on CSV files like this one, and up to 2x faster on tab-separated files. - Iterating over a file with
\r\nendings with the defaultnewline=Noneis 1.3x faster. csv.readeris 5% to 11% faster.
Lines of 5 to 15 characters also get faster. 2- and 4-byte strings and the other newline modes run the same code as before.
I have a PR ready.
Has this already been discussed elsewhere?
This is a minor feature, which does not need previous discussion elsewhere
Links to previous discussion of this feature:
No response
Activity
Merge branch 'main' into pythongh-161199-textio-line-endings
cmaloney commentedon Nov 7, 2026
I think this has significantly too much complexity for the performance and only adding this particular speedup in one place. It would be nicer to make the
stringliband general bytes search faster. https://github.com/python/cpython/pull/122902/changes for instance is similar but speeds up a broader range of cases.