Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

09 Mar 2021

The Racket Guide阅读笔记【十】

The Racket Guide阅读笔记,chapter 18, 19.

18 Concurrency and Synchronization

提供thread来支持并发。tread是抢占式的,也就是说一个thread抢占另一个thread,并不需要被抢占者同意。

18.1 Threads

下面的例子演示如何创建thread以及杀死thread:

(define worker (thread (lambda ()
                         (let loop ()
                           (displayln "Working...")
                           (sleep 0.2)
                           (loop)))))
(sleep 2.5)
(kill-thread worker)

主thread退出会导致程序退出,即便是其他线程还在运行。一个线程可以使用thread-wait去等待其他线程。

DrRacket中,主线程会一直运行知道Stop按键被按下,thread-wait并不是必须的。

18.2 Thread Mailboxes

每个thread有一个mailbox。thread-send可以发送消息给其他mailbox;thread-receive可以从mailbox获取最早的信息。

current_thread返回当前thread。

18.3 Semaphores

使用信号量可以协调对单一资源的非原子性访问。

  • make-semaphore用于创建信号量
  • semaphore-wait用于挂起等待信号量
  • semaphore-post用于释放信号量
  • call-with-semaphore是以上两者之结合

18.4 Channels

channel在两个thread之间搭建一条通道。事实上,多个thread可以从同一个channel里面获取内容。

  • make-channel用于创建channel
  • channel-get用于挂起等待
  • channel-put用于存放内容

18.5 Buffered Asynchronous Channels

带缓冲的异步channel。除非满了,否则put操作不会阻塞。此种意义下,有点像thread-send,不过允许多个消费者。

  • racket/async-channel
  • make-async-channel
  • async-channel-get
  • async-channel-put

18.6 Synchronizable Events and sync

sync可以作用于可同步的事件,比如channel, port, thread, alarm等等。

18.7 Building Your Own Synchronization Patterns

例子略。

19 Performance

19.1 Performance in DrRacket

DrRacket默认会给程序注入调试信息(Errortrace相关)。这会导致性能下降。

即便通过Choose Language…|Show Details禁用调试,但是perserve stacktrace勾选框也是默认被勾选上的,足以影响性能。

19.2 Racket Virtual Machine Implementations

有CS和BC两种实现:

  • CS是基于Chez Scheme的。(system-type 'vm)返回'chez-scheme并且(system-type)返回'cs
  • BC是8.0以前老的实现,(system-type 'vm)返回'racket。本身有两种变体
    • 3m,具有精确垃圾收集功能,(system-type 'gc)返回'3m
    • CGC是最老的,采用保守的垃圾收集。(system-type 'gc)返回’cgc

19.3 Bytecode, Machine Code, and Just-in-Time (JIT) Compilers

racket会将定义式和表达式转为bytecode格式,不同的实现有不同的bytecode格式。对于CS,bytecode既是目标机器码。raco make可以编译racket文件,生成bytecode文件。

通过current-compile-target-machine可以查看当前对口的机器码格式。

BC会将bytecode通过JIT编译转为机器码。通过eval-jit-enabled可以禁止jit。

19.4 Modules and Performance

模块系统为编译器提供了一些可用的假设,让编译器能够更好优化代码。

19.5 Function-Call Optimizations

编译器会主动进行inline操作。但是用户可以将函数包含在 begin-encourage-inline之中来主动提示编译器对函数进行inline。

19.6 Mutation and Performance

使用set!来更改变量绑定可能会导致性能降低,因为每次set!都会产生一个新的绑定。

19.7 letrec Performance

letrec用于绑定执行诀以及字面值的时候,效率较高。如果应用于其他形式的绑定,那么可能会影响性能。

19.8 Fixnum and Flonum Optimizations

19.9 Unchecked, Unsafe Operations

racket/unsafe/ops可以让racket放弃执行许多运行时对类型以及索引等信息的检查,在性能关键区域使用可以达到一定效果。

19.10 Foreign Pointers

ffi/unsafe提供了对指针的一些操作。racket会为其中的某些操作做优化。

19.11 Regular Expression Performance

对正则表达式进行预编译可以提供使用性能。可以使用#rx或者#px来指定正则表达式常量;也可以显示使用regexp来显示预编译。

19.12 Memory Management

CS和BC均使用更现代的隔代式垃圾收集器,可以让临时对象的分配和回收成本更小。CGC版本的BC使用保守的垃圾收集器,主要为了与C代码更好兼容,但是导致其准确度和运行速度有所欠缺。

另外编译器对自封诀进行一些优化,避免一些分配操作。

19.13 Reachability and Garbage Collection

racket提供make-weak-box以及weak-box-value来提供弱引用的支持。弱引用情况下,垃圾回收器可以回收弱引用所指向的值。

所有值都需要分配,下面的除外:

  • fixnum
  • 可预知使用场景的执行诀
  • 内有的符号名
  • CGC版本中模糊的可到达性

19.14 Weak Boxes and Testing

弱引用在测试中的特殊应用。

19.15 Reducing Garbage Collection Pauses

隔代垃圾收集器会区分常用对象和非常用对象。对于前者会经常检查,对于后者的检查频次较低,但是检查会导致程序暂停。

为了避免垠垃圾收集而导致的长时间的暂停,3m垃圾收集器支持渐进式垃圾回收,CS垃圾收集器实现了类似功能:

  • 3m的渐进模式下,小范围收集稍微被延长了一点,为大规模收集做一些准备。这样会大大降低大规模收集的时间,使其变得跟小规模收集差不多。这种情况下,垃圾收集的间断具有均匀分布的特性。
  • CS的渐进模式下,所有对象都是最近分配的,但是对最近这个概念有一定的分级。垃圾回收的时候会优先回收最最近的对象。

可以通过设置环境变量PLT_INCREMENTAL_GC来禁用渐进式垃圾收集。可以通过(collect-garbage 'incremental)来启动渐进式垃圾收集。通过执行 (collect-garbage)可以强制进行来及收集。

可以通过打日志来判断来判断垃圾收集导致的间断:

racket -W "debug@GC error" main.rkt

(本篇完)