Asynchronous programming has gained significant popularity over the last decade: support for this programming pattern is available in many popular languages via libraries and native language implementations, typically in the form of coroutines or the async/await construct. Instead of programming via shared memory, this concept assumes implicit synchronization through message passing. The key data structure enabling such communication is the rendezvous channel. Roughly, a rendezvous channel is a blocking queue of size zero, so both send(e) and receive() operations wait for each other, performing a rendezvous when they meet. To optimize the message passing pattern, channels are usually equipped with a fixed-size buffer, so send(e)-s do not suspend and put elements into the buffer until its capacity is exceeded. This primitive is known as a buffered channel. This paper presents a fast and scalable algorithm for both rendezvous and buffered channels. Similarly to modern queues, our solution is based on an infinite array with two positional counters for send(e) and receive() operations, leveraging the unconditional Fetch-And-Add instruction to update them. Yet, the algorithm requires non-trivial modifications of this classic pattern, in order to support the full channel semantics, such as buffering and cancellation of waiting requests. We compare the performance of our solution to that of the Kotlin implementation, as well as against other academic proposals, showing up to 9.8x speedup. To showcase its expressiveness and performance, we also integrated the proposed algorithm into the standard Kotlin Coroutines library, replacing the previous channel implementations.
翻译:近十年来,Async- 编程获得显著支持: 许多流行语言通过图书馆和本地语言实施方式支持这种编程模式, 通常是以共同的记忆或Async/wait 构建的形式。 这个概念不是通过共享的记忆来编程, 而是通过传递信息来隐含同步。 能够进行这种通信的关键数据结构是会合通道。 大致上, 一个会合频道是一个零号的阻塞队列, 因此, 发送( e) 和接收( ) 操作在相会时进行会合。 要优化信息传递模式, 频道通常配备固定大小的缓冲, 所以发送( e) 通常不会暂停, 并将元素放入缓冲, 直到它的能力被超过。 这个原始化的系统被称为缓冲通道。 本文为会合和缓冲通道提供了快速和可扩缩的算法。 和现代排队列一样, 我们的解决方案是基于一个无限的阵列, 有两个位置的计数, 发送(e) 接收( 接收) 操作, 利用无条件的发送和添加指令来更新它们。 但是, 发送, 发送, 发送(ereal- a- addal- add) 更新它们需要算, 将它需要非明示的缓冲进程 运行 运行 将运行 运行 运行 运行 运行 运行 运行 运行 运行 运行 运行 运行 运行 运行 运行 运行 以显示 运行 运行 运行 运行 运行 以显示 运行 以显示 以显示 运行为整个 常规化为 运行 运行 运行 运行 。