PasteRack.org
Paste # 46356
2019-09-14 03:00:17

Fork as a new paste.

Paste viewed 151 times.


Embed:

  1. #lang at-exp racket
  2.  
  3. (require ; racket/fixnum
  4.          racket/future
  5.          scribble/srcdoc
  6.          (for-doc scribble/manual
  7.                   scribble-math/dollar))
  8.  
  9. (require racket/require
  10.          (filtered-in
  11.           (λ (name) (regexp-replace #rx"unsafe-" name ""))
  12.           racket/unsafe/ops)
  13.          (only-in
  14.           racket/fixnum
  15.           make-fxvector
  16.           fxvector?))
  17.  
  18. (provide
  19.  
  20.  (proc-doc
  21.   fxvector-futures-sort!
  22.   (->i ((unsorted fxvector?))
  23.        ((compare procedure?)
  24.         #:progress-proc (progress-proc (or/c procedure? false?))
  25.         #:progress-sleep (progress-sleep flonum?))
  26.        (res fxvector?))
  27.   ((λ (a b) (fx< a b)) #f 0.1)
  28.   @{
  29.  
  30.     Sorts fxvector in place. If progress-proc is not #f, calls it
  31. every progress-sleep seconds.
  32.  
  33.     The procedure uses merge sort with @${n} merge operations. Its
  34. overall algorithmic time complexity is @${O(n\cdot\log_2 n)} and
  35. memory complexity is @${O(n)}. The actual memory consumption is @${8n}
  36. in addition to input of the same size on 64bit platforms.
  37.  
  38. The implementation uses all available processors.
  39.  
  40. If a custom compare function is provided, it should be a lambda term
  41. and not a reference to some other function. For example, providing fx<
  42. as compare blocks running in parallel, but using the default compare
  43. function as is provides support for maximum parallelism.
  44.  
  45.     })
  46.  
  47.  (rename-out (futures-depth fxvector-util-futures-depth)
  48.              (num-futures fxvector-util-num-futures))
  49.  
  50.  )
  51.  
  52. (define processors (processor-count))
  53. (define futures-depth (inexact->exact (ceiling (log processors 2))))
  54. (define num-futures (expt 2 futures-depth))
  55.  
  56. (define (fxvector-futures-sort! unsorted
  57.                                 (compare (λ (a b) (fx< a b)))
  58.                                 #:progress-proc (progress-proc #f)
  59.                                 #:progress-sleep (progress-sleep 0.1))
  60.   (define unsorted-length (fxvector-length unsorted))
  61.   (define scratchpad (make-fxvector unsorted-length))
  62.  
  63.   (define total-merges (fx- unsorted-length 1))
  64.  
  65.   ; Create progresses vector, start progress thread and create stop
  66.   ; procedure
  67.   (define (create-progresses+stop)
  68.     (define progresses (make-fxvector num-futures))
  69.     (define progress-running #t)
  70.     (define progress-thread
  71.       (thread
  72.        (λ ()
  73.          (let loop ()
  74.            (when progress-running
  75.              (define progress
  76.                (let loop ((i 0)
  77.                           (acc 0))
  78.                  (if (fx< i num-futures)
  79.                      (loop (fx+ i 1)
  80.                            (fx+ acc (fxvector-ref progresses i)))
  81.                      acc)))
  82.              (progress-proc progress total-merges)
  83.              (sleep progress-sleep)
  84.              (loop))))))
  85.  
  86.     ; Stops the loop and waits for the thread
  87.     (define (progress-stop)
  88.       (set! progress-running #f)
  89.       (thread-wait progress-thread)
  90.       (progress-proc total-merges total-merges))
  91.  
  92.     ; Must be assigned at once
  93.     (values progresses progress-stop))
  94.  
  95.   ; Create progresses vector and stop proc if required
  96.   (define-values
  97.     (progresses progress-stop)
  98.     (if progress-proc
  99.         (create-progresses+stop)
  100.         (values #f #f)))
  101.  
  102.   ; Generic merge stage
  103.   (define (merge! from1 to1/from2 to2 depth fid)
  104.     (when progress-proc
  105.       (fxvector-set! progresses fid (fx+ (fxvector-ref progresses fid) 1)))
  106.     (define src (if (fx= (fxand depth 1) 1) unsorted scratchpad))
  107.     (define dst (if (fx= (fxand depth 1) 1) scratchpad unsorted))
  108.     (let loop1 ((i from1)
  109.                 (j to1/from2)
  110.                 (k from1))
  111.       ; Always select correct element based on compare function
  112.       (cond
  113.         ((and (fx< i to1/from2)
  114.               (fx< j to2))
  115.          (define v1 (fxvector-ref src i))
  116.          (define v2 (fxvector-ref src j))
  117.          (cond
  118.            ((compare v1 v2)
  119.             (fxvector-set! dst k v1)
  120.             (loop1 (fx+ i 1) j (fx+ k 1)))
  121.            (else
  122.             (fxvector-set! dst k v2)
  123.             (loop1 i (fx+ j 1) (fx+ k 1)))))
  124.         (else
  125.          (let loop2 ((i i)
  126.                      (j j)
  127.                      (k k))
  128.            ; Finish from the first part
  129.            (cond
  130.              ((fx< i to1/from2)
  131.               (fxvector-set! dst k (fxvector-ref src i))
  132.               (loop2 (fx+ i 1) j (fx+ k 1)))
  133.              (else
  134.               (let loop3 ((i i)
  135.                           (j j)
  136.                           (k k))
  137.                 ; Finish from the second part
  138.                 (when (fx< j to2)
  139.                   (fxvector-set! dst k (fxvector-ref src j))
  140.                   (loop3 i (fx+ j 1) (fx+ k 1)))))))))))
  141.  
  142.   (define (sort-step from to (depth 0) (fid 0))
  143.     (define cnt (fx- to from))
  144.     (cond ((fx> cnt 2)
  145.            ; >2 means we do a proper split/merge
  146.            ; this is the only part to leverage futures
  147.            (define cnt2 (fxrshift cnt 1))
  148.            (define from1 from)
  149.            (define to1/from2 (fx+ from cnt2))
  150.            (define to2 to)
  151.            (cond ((fx< depth futures-depth)
  152.                   (let ((f1 (future
  153.                              (λ ()
  154.                                (sort-step from1 to1/from2
  155.                                           (fx+ depth 1)
  156.                                           fid)
  157.                                #f)))
  158.                         (f2 (future
  159.                              (λ ()
  160.                                (sort-step to1/from2 to2
  161.                                           (fx+ depth 1)
  162.                                           (fxior fid (fxlshift 1 depth)))
  163.                                #f))))
  164.                     (or (touch f1)
  165.                         (touch f2))))
  166.                  (else
  167.                   (sort-step from1 to1/from2 (fx+ depth 1) fid)
  168.                   (sort-step to1/from2 to2 (fx+ depth 1) fid)
  169.                   ))
  170.            (merge! from1 to1/from2 to2 depth fid))
  171.           ((fx= cnt 2)
  172.            (when progress-proc
  173.              (fxvector-set! progresses fid (fx+ (fxvector-ref progresses fid) 1)))
  174.            ; =2 - swap in-place or from the other
  175.            (define v1 (fxvector-ref unsorted from))
  176.            (define v2 (fxvector-ref unsorted (fx+ from 1)))
  177.            (define dst (if (fx= (fxand depth 1) 1) scratchpad unsorted))
  178.            (define v1first (compare v1 v2))
  179.            (fxvector-set! dst
  180.                           from
  181.                           (if v1first v1 v2))
  182.            (fxvector-set! dst
  183.                           (fx+ from 1)
  184.                           (if v1first v2 v1)))
  185.           ((fx= cnt 1)
  186.            ; =1 - only copy if it must go to scratchpad
  187.            (when (fx= (fxand depth 1) 1)
  188.              (fxvector-set! scratchpad from (fxvector-ref unsorted from))))
  189.           ))
  190.  
  191.   ; Start with whole fxvector
  192.   (sort-step 0 unsorted-length)
  193.  
  194.   ; Stop progress thread
  195.   (when progress-proc
  196.     (progress-stop))
  197.  
  198.   ; Return result
  199.   unsorted)

=>