branch: externals/el-job
commit c16011c96d0ed69b36a552e9e4f7ed4e003fbb2d
Author: Martin Edström <[email protected]>
Commit: Martin Edström <[email protected]>

    Fix internal error, toggle do-bench instead of signaling
---
 el-job-ng.el | 50 +++++++++++++++++++++++++++++++-------------------
 1 file changed, 31 insertions(+), 19 deletions(-)

diff --git a/el-job-ng.el b/el-job-ng.el
index b4d57232a5..0a9c545331 100644
--- a/el-job-ng.el
+++ b/el-job-ng.el
@@ -57,6 +57,7 @@ to unlock this message."
   "Split LIST into up to N-SLICES sublists."
   (and list (seq-split list (ceiling (/ (length list) (float n-slices))))))
 
+(defvar el-job-ng--splitter-complaint nil)
 (defun el-job-ng--split-optimally (list n-slices benchmarks)
   "Split LIST into up to N-SLICES sublists.
 
@@ -81,7 +82,7 @@ In other words, this equals LIST:
            (items-and-durations
             (cl-loop for item in list
                      as benchmark = (gethash item benchmarks)
-                     collect (cons item (and benchmark (float-time benchmark)))
+                     collect (cons item benchmark)
                      if benchmark
                      do (progn
                           (cl-incf n-benchmarks)
@@ -100,26 +101,32 @@ In other words, this equals LIST:
            while items-and-durations
            as (item . dur) = (pop items-and-durations)
            do (progn
-                (when (and sublist dur (> dur max-per-slice))
-                  ;; An item exceeding max by itself must get a dedicated 
process,
-                  ;; because we will likely be still waiting on that process 
after
-                  ;; the rest have finished, even with the dedication!
+                (when (and sublist dur (time-less-p max-per-slice dur))
+                  ;; An item exceeding max by itself must get a dedicated
+                  ;; process, because we will likely be still waiting on that
+                  ;; process after the rest have finished.
+                  ;; That's common for power-law distributed things.
                   ;; So finish the current sublist early, it's worth it.
                   (push (nreverse sublist) sublists)
                   (setq sublist nil)
                   (setq sum 0))
                 (push item sublist)
-                (cl-incf sum (or dur dur-mean))
-                (when (> sum max-per-slice)
+                (setq sum (time-add sum (or dur dur-mean)))
+                (when (time-less-p max-per-slice sum)
                   (push (nreverse sublist) sublists)
                   (setq sublist nil)
                   (setq sum 0)))
            finally do (when sublist
                         (push (nreverse sublist) sublists)))
-          (when (> (length sublists) n-slices)
-            (fset #'el-job-ng--split-optimally #'el-job-ng--split-evenly)
-            (error "el-job: Internal error, degrading gracefully from now on"))
-          (nreverse sublists)))))))
+          (if (<= (length sublists) n-slices)
+              (nreverse sublists)
+            ;; It is possible to have e.g. `n-slices'==8, a million tiny items,
+            ;; and 9 items big enough to exceed `max-per-slice', giving us 10
+            ;; sublists in the best case, if not upwards to 20 depending on
+            ;; how those items are ordered.
+            ;; Just give up in this aberrant case.
+            (setq el-job-ng--splitter-complaint t)
+            (el-job-ng--split-evenly list n-slices))))))))
 
 (defun el-job-ng--locate-lib (name)
   "Try to find the full .eln or .elc filename for library NAME.
@@ -138,6 +145,7 @@ Unlike `locate-library', this can actually find the .eln."
   ((id              :initarg :id)
    (stderr          :initform nil)
    (callback        :initform nil)
+   (do-bench        :initform t)
    (process-outputs :initform nil)
    (benchmarks      :initform (make-hash-table :test 'equal))))
 
@@ -209,7 +217,7 @@ ID can also be passed to these helpers:
   (let ((job (with-memoization (gethash id el-job-ng--jobs)
                (make-instance 'el-job-ng-job :id id))))
     (oset job callback callback)
-    (with-slots (process-outputs benchmarks stderr) job
+    (with-slots (process-outputs stderr benchmarks do-bench) job
       ;; Cancel any currently-running job with same ID
       (while-let ((proc (car (pop process-outputs))))
         (delete-process proc))
@@ -227,9 +235,13 @@ ID can also be passed to these helpers:
                (forms (prin1-to-string eval))
                (func (prin1-to-string funcall-per-input))
                (input-sets
-                (el-job-ng--split-optimally inputs
-                                            el-job-ng-max-cores
-                                            benchmarks))
+                (prog1 (el-job-ng--split-optimally inputs
+                                                   el-job-ng-max-cores
+                                                   benchmarks)
+                  (when el-job-ng--splitter-complaint
+                    (setq el-job-ng--splitter-complaint nil)
+                    (setf do-bench nil)
+                    (clrhash benchmarks))))
                (n (length input-sets))
                ;; Ensure a local working directory.
                ;; https://github.com/meedstrom/org-node/issues/46
@@ -342,18 +354,18 @@ and run `el-job-ng--handle-finished-child'."
            (el-job-ng-kill-keep-bufs id)))))
 
 (defun el-job-ng--handle-finished-child (proc buf job)
-  (with-slots (id process-outputs callback benchmarks) job
+  (with-slots (id process-outputs callback benchmarks do-bench) job
     (with-current-buffer buf
       (unless (and (eobp) (> (point) 2) (eq (char-before) ?\n))
         (error "Process output looks incomplete or point moved"))
       (setcdr (assq proc process-outputs)
               (cl-loop for (input benchmark output) in (read (buffer-string))
-                       do (puthash input benchmark benchmarks)
-                       and collect output))
+                       when do-bench do (puthash input benchmark benchmarks)
+                       collect output))
       (setcar (assq proc process-outputs) nil)
       (when (= 0 el-job-ng--debug-level)
         (kill-buffer)))
-    ;; Last child
+    ;; True when this was the last child to finish.
     (when (and callback (cl-every #'null (mapcar #'car process-outputs)))
       (let ((outputs (prog1 (mapcan #'cdr process-outputs)
                        (setf process-outputs nil))))

Reply via email to