当前位置: 首页 > 工具软件 > newLISP > 使用案例 >

newLISP 多核编程

贾烨
2023-12-01

newLISP实现了Cilk API, 可以将多个同时运行的进程执行在多处理器或者多核的CPU架构上,已达到并行节省运行时间的目的。

使用spawn和sync函数可以很快的实现。下面是来自newLISP官方文档的例子, 我增加了一行代码

#!/usr/bin/newlisp

; calculate primes in a range
(define (primes from to)
  (local (plist)
      (for (i from to)
          (if (= 1 (length (factor i)))
              (push i plist -1)))
      plist))

; start child processes
(set 'start (time-of-day))

(spawn 'p1 (primes 1 1000000))
(spawn 'p2 (primes 1000001 2000000))
(spawn 'p3 (primes 2000001 3000000))
(spawn 'p4 (primes 3000001 4000000))

; wait for a maximum of 60 seconds for all tasks to finish
(sync 60000) ; returns true if all finished in time
; p1, p2, p3 and p4 now each contain a lists of primes

(println "time spawn: " (- (time-of-day) start))
(println "time simple: " (time  (primes 1 4000000)))

(println p1)
(exit)


运行结果是:

time spawn: 5068
time simple: 8614.983
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 
 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 
 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 


现在来解释一下上面的代码:

1. local

(local是一个和(let)差不多的函数,用来初始化变量,区别是(local)将变量都初始化为nil,因此plist的初始值为nil

2. spawn 函数

syntax: (spawn sym exp [true])

sym 用来保存创建的子进程ID

exp 是子进程启动后运行的表达式,这里我们传递的是函数primes构成的表达式

true 是当父子进程通过send/receive函数通信时才需要设置,一般不需要使用该参数


spawn函数会立刻返回,所以等待子进程返回需要使用sync函数。不过要注意,sync函数返回后,子进程计算结果会保存到sym中。

3. sync函数

syntax: (sync int-timeout [func-inlet])
syntax: (sync)

int-timeout 指定等待毫秒数,如上面的60000毫秒,也就是60秒。如果指定时间到达而子进程也结束,sync返回true, 如果子进程届时还没有结束,则sync返回nil.

不管子进程是否完成,sync返回后, 子进程计算结果也会保存到之前spawn返回的pid上。

func-inlet 是可以在sync返回的时候执行的函数或者lambda表达式,参数是子进程id.


4. 用sync来跟踪进度

sync接收的超时时间只是一个估值,可以通过在循环里面多次调用,并指定较短的超时时间来

将sync代码修改一下,变为如下:

; print a dot after each 1 seconds of waiting
(until (sync 1000) (print "."))

运行结果为:

.....time spawn: 5147

5. 失败处理

可能有些子进程由于遇到问题,就是不能按时结束,可以在sync中设置一个认为合理的超时时间,之后用abort关闭哪些没有结束的子进程。

(spawn 'p1 (primes 1 1000000))
(spawn 'p2 (primes 1000001 2000000))
(spawn 'p3 (primes 2000001 3000000))
(spawn 'p4 (primes 3000001 4000000))

; wait for one minute, then abort and
; report unfinished PIDs

(if (not (sync 60000))
    (begin
        (println "aborting unfinished: " (sync))
        (abort))
    (println "all finished successfully")
)

注意,这里的(sync) 返回的是还没有结束的子进程ID.


6. 递归下使用

特别适合我的需要,我有一棵任务树,采用广度优先算法递归遍历该树后,为每个节点创建一个进程来执行任务,直到所有节点执行完后,才执行根节点的任务。

由于比较复杂,就不在这里附上我的代码,仅演示官方文档中的fibannaci算法。

#!/usr/bin/newlisp

(define (fibo n)
  (local (f1 f2)
    (if(< n 2) 1
       (begin
          (spawn 'f1 (fibo (- n 1)))
          (spawn 'f2 (fibo (- n 2)))
          (sync 10000)
          (+ f1 f2)))))

(println (fibo 12))

(exit)

执行结果是

233




 类似资料: