Самый быстрый способ прочитать много входных данных в PyPy3 и что здесь делает BytesIO? - PullRequest
0 голосов
/ 09 марта 2020

Недавно я работал над проблемой, которая требовала, чтобы я прочитал много строк чисел (около 500 000).

Вначале я обнаружил, что использование input () было слишком медленным. Использование stdin.readline () было намного лучше. Однако, это все еще не было достаточно быстро. Я обнаружил, что использование следующего кода:

import io, os
input = io.BytesIO(os.read(0,os.fstat(0).st_size)).readline

и использование input () таким образом улучшило время выполнения. Тем не менее, я на самом деле не понимаю, как работает этот код. Чтение документации для os.read, 0 в os.read (0, os.fstat (0) .st_size) описывает файл, из которого мы читаем. Какой файл 0 описывает? Кроме того, fstat описывает состояние файла, из которого мы читаем, но очевидно, что ввод означает максимальное количество байтов, которые мы читаем?

Код работает, но я хочу понять, что он делает и почему он быстрее. Любая помощь приветствуется.

Ответы [ 2 ]

3 голосов
/ 09 марта 2020

0 - дескриптор файла для стандартного ввода. os.fstat(0).st_size сообщит Python, сколько байтов в настоящее время ожидает в стандартном входном буфере. Затем os.read(0, ...) будет считывать большое количество байтов, опять же из стандартного ввода, создавая строку байтов.

(В качестве дополнительного примечания, 1 - файловый дескриптор стандартного вывода, а 2 - стандартный ошибка.)

Вот демонстрационный пример:

echo "five" | python3 -c "import os; print(os.stat(0).st_size)"
# => 5

Python обнаружил четыре однобайтовых символа и символ новой строки в стандартном входном буфере и сообщил о пяти байтах, ожидающих чтения.

С байтовыми строками не очень удобно работать, если вам нужен текст - во-первых, они не совсем понимают концепцию «строк» ​​- поэтому BytesIO подделывает входной поток с переданной байтовой строкой, позволяя вам readline от него. Я не уверен на 100%, почему это быстрее, но мои предположения таковы:

  • Нормальное чтение, вероятно, выполняется посимвольно, так что можно обнаружить разрыв строки и остановить ее, не читая слишком много; массовое чтение более эффективно (и поиск новых строк постфактум в памяти довольно быстро)
  • Таким образом, обработка кодирования не выполняется
2 голосов
/ 09 марта 2020

os.read имеет подпись Я звоню fd, size. Установка size в байтах, оставленных в fd, заставляет все остальное налетать на вас, как туснами. Есть также «стандартные файловые дескрипторы» для 0 = стандартный ввод, 1 = стандартный вывод, 2 = стандартный вывод.

Деконструкция кода:

import io, os # Utilities
input = \ # Replace the input built-in
  io.BytesIO( \ # Create a fake file
    os.read( \ # Read data from a file descriptor
      0, \ # stdin
      os.fstat(0) \ # Information about stdin
        .st_size \ # Bytes left in the file
    )
  ) \
  .readline # When called, gets a line of the file
...