Pertanyaan Mengapa pmap | reducer / map tidak menggunakan semua core cpu?


Saya mencoba mem-parsing file dengan jutaan baris, setiap baris adalah string json dengan beberapa informasi tentang buku (penulis, konten, dll). saya menggunakan iota untuk memuat file, karena program saya melempar OutOfMemoryError jika saya mencoba menggunakannya slurp. Saya juga menggunakan cheshire untuk menguraikan string. Program ini hanya memuat file dan menghitung semua kata di semua buku.

Upaya pertama saya termasuk pmap untuk melakukan pekerjaan berat, saya pikir ini pada dasarnya akan menggunakan semua core cpu saya.

(ns multicore-parsing.core
  (:require [cheshire.core :as json]
            [iota :as io]
            [clojure.string :as string]
            [clojure.core.reducers :as r]))


(defn words-pmap
  [filename]
  (letfn [(parse-with-keywords [str]
            (json/parse-string str true))
          (words [book]
            (string/split (:contents book) #"\s+"))]
    (->>
     (io/vec filename)
     (pmap parse-with-keywords)
     (pmap words)
     (r/reduce #(apply conj %1 %2) #{})
     (count))))

Meskipun tampaknya menggunakan semua inti, masing-masing inti jarang menggunakan lebih dari 50% dari kapasitasnya, tebakan saya adalah bahwa itu ada hubungannya dengan ukuran batch pmap dan jadi saya menemukan pertanyaan yang relatif lama di mana beberapa komentar mengacu pada clojure.core.reducers Perpustakaan.

Saya memutuskan untuk menulis ulang fungsi menggunakan reducers/map:

(defn words-reducers
  [filename]
  (letfn [(parse-with-keywords [str]
            (json/parse-string str true))
          (words [book]
            (string/split (:contents book) #"\s+"))]
  (->>
   (io/vec filename)
   (r/map parse-with-keywords)
   (r/map words)
   (r/reduce #(apply conj %1 %2) #{})
   (count))))

Tetapi penggunaan cpu lebih buruk, dan bahkan membutuhkan waktu lebih lama untuk menyelesaikan dibandingkan dengan implementasi sebelumnya:

multicore-parsing.core=> (time (words-pmap "./dummy_data.txt"))
"Elapsed time: 20899.088919 msecs"
546
multicore-parsing.core=> (time (words-reducers "./dummy_data.txt"))
"Elapsed time: 28790.976455 msecs"
546

Apa yang saya lakukan salah? Apakah mmap memuat + reduksi pendekatan yang benar saat mengurai file besar?

EDIT: ini adalah file yang saya gunakan.

EDIT2: Berikut pengaturan waktunya iota/seq dari pada iota/vec:

multicore-parsing.core=> (time (words-reducers "./dummy_data.txt"))
"Elapsed time: 160981.224565 msecs"
546
multicore-parsing.core=> (time (words-pmap "./dummy_data.txt"))
"Elapsed time: 160296.482722 msecs"
546

5
2018-05-14 17:10


asal


Jawaban:


Saya tidak percaya bahwa reduksi akan menjadi solusi yang tepat untuk Anda, karena mereka tidak mengatasi dengan urutan malas sama sekali dengan baik (peredam akan memberikan hasil yang benar dengan urutan malas, tetapi tidak akan sejajar dengan baik).

Anda mungkin ingin melihat ini Kode sampel dari buku itu Tujuh Model Concurrency dalam Tujuh Minggu (disclaimer: I am the author) yang memecahkan masalah yang sama (menghitung berapa kali setiap kata muncul di Wikipedia).

Diberikan daftar halaman Wikipedia, fungsi ini menghitung kata-kata secara berurutan (get-words mengembalikan urutan kata dari suatu halaman):

(defn count-words-sequential [pages]
  (frequencies (mapcat get-words pages)))

Ini adalah versi paralel menggunakan pmap yang berjalan lebih cepat, tetapi hanya sekitar 1,5x lebih cepat:

(defn count-words-parallel [pages]
  (reduce (partial merge-with +)
    (pmap #(frequencies (get-words %)) pages)))

Alasannya hanya sekitar 1.5x lebih cepat karena reduce menjadi hambatan — itu namanya menelepon (partial merge-with +) satu kali untuk setiap halaman. Penggabungan sejumlah 100 halaman meningkatkan kinerja menjadi sekitar 3,2x pada mesin 4-inti:

(defn count-words [pages]
  (reduce (partial merge-with +)
    (pmap count-words-sequential (partition-all 100 pages))))

3
2018-05-16 16:24