Коротко говоря, мне нужно обработать 100 гигабайт журналов в странном формате, а затем провести некоторый анализ результатов.
Выбрал CL в качестве своего инструмента, потому что я уже «комфортно» с ним работаю, несмотря на то, что раньше не использовал его для сценариев, подобных оболочке.
Начальные части синтаксического анализа и CLI выполнены, попробовали некоторые тестовые данные с 1 ГБ, и это заняло около минуты. Винил PPCRE, боже, мой алгоритм, но в конце концов провел проверку работоспособности, которая просто скопировала *стандартный ввод* в *стандартный вывод*, и она показала, что большая часть времени тратится на чтение. Python сделал то же самое за пару секунд, если вообще.
Чтобы сгенерировать пример данных:
yes "$(printf 'A%.0s' {1..10})" | head -c 1G > sample.txt
Общий код LISP:
#!/usr/bin/env -S sbcl --script
(defun main ()
(loop for line = (read-line *standard-input* nil)
while line
do (write-string line)
(write-char #\NEWLINE)))
(eval-when (:execute)
(main))
Код Python:
#!/usr/bin/env python3
import sys
def main():
for line in sys.stdin:
sys.stdout.write(line)
if __name__ == "__main__":
main()
Для запуска:
time cat data/sample.txt | ./test.lisp > result_lisp.txt
# real 1m59.719s
# user 0m47.231s
# sys 1m13.008s
time cat data/sample.txt | ./test.py > result_python.txt
# real 0m9.557s
# user 0m7.688s
# sys 0m2.144s
Версия SBCL: SBCL 2.2.9.debian.
Версия Python: Python 3.13.3.
Вопрос: существует ли обходной путь или исправление этой проблемы? До сих пор CL ни разу не подвел меня с точки зрения производительности, даже при выполнении некоторых сложных вычислений он обычно был быстрее, чем Python.
==EDIT==
Решил профилировать аналогичный фрагмент, как и оригиналы, теперь он буквально ничего не делает, кроме чтения строки.
#!/usr/bin/env -S sbcl --script
(require :sb-sprof)
(defun main ()
(loop for line = (read-line *standard-input* nil)
while line
))
(eval-when (:execute)
(sb-sprof:with-profiling (:max-samples 100000 :sample-interval 0.00001 :report :graph)
(main)))
Похоже, что большая часть времени уходит на преобразование UTF.
Self Total Cumul
Nr Count % Count % Count % Calls Function
------------------------------------------------------------------------
1 12930 49.2 13191 50.2 12930 49.2 - SB-IMPL::INPUT-CHAR/UTF-8
2 9724 37.0 22523 85.7 22654 86.2 - (LAMBDA (&REST REST) :IN SB-IMPL::GET-EXTERNAL-FORMAT)
3 2517 9.6 25834 98.3 25171 95.8 - READ-LINE
4 146 0.6 146 0.6 25317 96.3 - foreign function pthread_sigmask
5 26 0.1 26257 99.9 25343 96.4 - MAIN
6 26 0.1 26 0.1 25369 96.5 - RESTORE-YMM
7 6 0.0 262 1.0 25375 96.5 - SB-IMPL::REFILL-INPUT-BUFFER
8 5 0.0 214 0.8 25380 96.6 - (FLET "WITHOUT-INTERRUPTS-BODY-2" :IN SB-IMPL::REFILL-INPUT-BUFFER)
9 5 0.0 5 0.0 25385 96.6 - SAVE-YMM
10 4 0.0 617 2.3 25389 96.6 - ALLOC-TRAMP
Избавление от *standard-input* (действительно плохо для сценариев, подобных оболочке), по-видимому, ОЧЕНЬ повышает скорость и вызывает некоторые другие вещи, связанные с UTF.
(require :sb-sprof)
(defun main (str)
(loop for line = (read-line str nil)
while line))
(eval-when (:execute)
(sb-sprof:with-profiling (:max-samples 100000 :sample-interval 0.00001 :report :graph)
(with-open-file (str "sample.txt")
(main str))))
Self Total Cumul
Nr Count % Count % Count % Calls Function
------------------------------------------------------------------------
1 2133 38.7 2395 43.4 2133 38.7 - SB-IMPL::FD-STREAM-READ-N-CHARACTERS/UTF-8
2 763 13.8 5212 94.5 2896 52.5 - SB-IMPL::ANSI-STREAM-READ-LINE-FROM-FRC-BUFFER
3 725 13.1 725 13.1 3621 65.6 - SB-KERNEL:UB32-BASH-COPY
4 435 7.9 1882 34.1 4056 73.5 - (LABELS SB-IMPL::BUILD-RESULT :IN SB-IMPL::ANSI-STREAM-READ-LINE-FROM-FRC-BUFFER)
5 261 4.7 261 4.7 4317 78.2 - READ-LINE
6 119 2.2 119 2.2 4436 80.4 - foreign function pthread_sigmask
7 33 0.6 5516 100.0 4469 81.0 - MAIN
8 16 0.3 2413 43.7 4485 81.3 - SB-INT:FAST-READ-CHAR-REFILL
9 15 0.3 15 0.3 4500 81.6 - RESTORE-YMM
10 11 0.2 11 0.2 4511 81.8 - SAVE-YMM
Подробнее здесь: https://stackoverflow.com/questions/797 ... dard-input