Clojure: поток ввода медленнее, чем читатель

Я пытаюсь читать байты из потока ввода, и это намного медленнее, чем чтение символов с помощью считывателя. Я не могу понять, почему это так. Посмотрите на тест:

(defn r1
  [input]
  (loop []
    (when-not (= -1 (.read ^java.io.InputStream input))
      (recur))))

(defn r2
  [input]
  (loop []
    (when-not (.read input)
      (recur))))

(dotimes [_ 10] 
   (time (with-open [is (clojure.java.io/input-stream "15mb.log")]
     (r1 is))))

"Elapsed time: 111.608991 msecs"
"Elapsed time: 95.45663 msecs"
"Elapsed time: 148.789867 msecs"
"Elapsed time: 97.580527 msecs"
"Elapsed time: 113.093759 msecs"
"Elapsed time: 108.306019 msecs"
"Elapsed time: 107.71069 msecs"
"Elapsed time: 104.833343 msecs"
"Elapsed time: 174.701027 msecs"
"Elapsed time: 141.969629 msecs"

(dotimes [_ 10]
   (time (with-open [r (clojure.java.io/reader "15mb.log")]
      (r2 r))))

"Elapsed time: 0.635769 msecs"
"Elapsed time: 0.422315 msecs"
"Elapsed time: 0.355953 msecs"
"Elapsed time: 0.336128 msecs"
"Elapsed time: 0.333523 msecs"
"Elapsed time: 0.339613 msecs"
"Elapsed time: 0.329693 msecs"
"Elapsed time: 0.234213 msecs"
"Elapsed time: 0.209742 msecs"
"Elapsed time: 0.199334 msecs"

Насколько я знаю, clojure.java.io/input-stream использует BufferedInputStream, а clojure.java.io/reader использует BufferedReader, поэтому нет причин для такой резкой разницы в скорости. Я что-то пропустил?


person Eugene Aksenov    schedule 16.03.2017    source источник
comment
Вы уверены, что ваш r2 правильный? Разве вы не использовали .readLine там? (проверка ложности результата вместо сравнения с -1 укажет на это)   -  person Piotrek Bzdyl    schedule 16.03.2017
comment
Да, r2 неверен. Спасибо.   -  person Eugene Aksenov    schedule 17.03.2017


Ответы (1)


Ваш тест ошибочен. И BufferedReader, и BufferedInputStream возвращают -1 в конце потока. Таким образом, ваш тест на r2 также должен быть (when-not (= -1 (.read ....

Хотя приведенный ниже метод тестирования не точен до очень малых миллисекундных уровней, он достаточно точен для этого теста, и тест с использованием очень хорошей библиотеки тестов criterium для clojure дает аналогичные результаты. Повторная публикация теста более компактно, для удобства копирования/вставки:

(let [testfile "zerofile"]    ; $ dd if=/dev/zero of=zerofile bs=1k count=1k
  (map (fn [func label]
         (println label)
         (dotimes [_ 3]
           (time (with-open [data (func testfile)]
                   (while (not= -1 (.read data)))))))
    [clojure.java.io/input-stream,  clojure.java.io/reader]
    ["Input Stream:" "\nReader:"]))

Один результат:

Input Stream:
"Elapsed time: 624.01494 msecs"
"Elapsed time: 650.407183 msecs"
"Elapsed time: 627.244097 msecs"

Reader:
"Elapsed time: 706.776733 msecs"
"Elapsed time: 691.887275 msecs"
"Elapsed time: 703.918226 msecs"
person Josh    schedule 16.03.2017
comment
Спасибо за помощь, теперь вижу. Я слишком рано выхожу со считывателем в моем примере. - person Eugene Aksenov; 17.03.2017