離跟 GIL 說再見更進一步的 Free-threaded CPython

在「Free-threaded CPython is ready to experiment with (quansight.org)」這邊看到 PythonGIL 的進度,從原文「Free-threaded CPython is ready to experiment with!」這邊可以看到有些需要套件一起配合的,像是 NumPy 這邊的「BUG: error Python 3.13 free-threaded RuntimeError: Identity cache already includes the item.」。

看起來用到 C 的實作 (大多數應該是為了加速,或是需要底層的 syscall) 都會需要注意一下。

另外值得一提的是目前拔掉 GIL 的版本在 single threading 的情況下會比標準版的 Python 3 慢很多,會需要後續再改進,所以一開始的版本就算穩定下來也可能沒有經濟上的效益,參考 id=40949628 這邊提到的:

Right now there is a significant single-threaded performance cost. Somewhere from 30-50%. Part of what my colleague Ken Jin and others are working on is getting back some of that lost performance by applying some optimizations. Expect single-threaded performance to improve for Python 3.14 next year.

另外有看到一些有趣的討論,像是為什麼不 bump 版本號碼到 Python 4 (因為語法沒有大改變?)。

Java 21 (LTS) 推出

Hacker News 上注意到 Java 21 的消息:「JDK 21 Release Notes」、「Java 21 / JDK 21: General Availability」、「OpenJDK JDK 21 General-Availability Release」。

對於沒什麼在寫 Java 的人來說 (也是等於比較沒有在接觸 Java 圈子消息的人),比較意外的是這是推出的是 LTS 版本,距離上次的 LTS (Java 17) 才兩年前 (2021/09/14):

JDK 21 will be a long-term-support (LTS) release from most vendors, including Oracle. If you’re upgrading from the previous LTS release, JDK 17, then you have many more JEPs to look forward to, summarized here:

翻了一下 Java version history,可以知道同時支援的 LTS 版本變成四個了,而最近一次會終止的會是 Java 11,Red Hat 會在 2024/10 終止,而 Oracle 會在 2026/09 終止,這中間還會不會再增加 LTS...?

雖然沒什麼在寫,但還是常常會看到有人提到這次引入了 Virtual ThreadsGenerational ZGC,這應該是討論度最高的。

看了 Virtual Threads 的說明,有種「反璞歸真」的感覺...

在二十年前的時候,就已經有很多 userland threading library,讓應用程式可以用 threading design pattern 開發程式,而當時 x86 下的作業系統開始要遇到多 CPU 的環境,才開始在 kernel 裡支援 threading,讓應用程式裡面的 threading 可以打散到多個不同的 CPU 上面。

記得當年 FreeBSD 4 之後對 SMP 與 threading 的爭論導致分家出 DragonflyBSD,而 FreeBSD 的多 CPU 效能與穩定性要一直到 FreeBSD 7 才穩了下來。

現在 Java 反過來為了降低 OS thread 造成的 overhead,讓 java.lang.Thread 可以跑在 userland 裡面,不要用 kernel 提供的 OS thread...

另外又讓我想到 kqueueepoll 以及 libevent 的事情了,不過這扯遠了...

Python 的原生 multithreading 支援

Simon Willison 這邊看到的:「Real Multithreading is Coming to Python - Learn How You Can Use It Now」,他引用的原文在「Real Multithreading is Coming to Python - Learn How You Can Use It Now」這邊,在講 Python 3.12 將會有原生 multithreading 支援。

Python 裡知名的 GIL 問題將會用 Per-Interpreter GIL 的技術來解決,把 GIL 的 global-based 改寫變成 thread-based:

With introduction of "Per-Interpreter GIL", individual Python interpreters don't share the same GIL anymore. This level of isolation allows each of these sub-interpreters to run really concurrently.

這算是基礎建設,之後應該會有蠻長的陣痛期要轉換,尤其是各個其他程式語言寫的 library 要考慮到 thread-safe 的問題。

話說回來,PHP 就沒繼續討論過 threading 這個問題了,大家還是繼續用 process 架構在搞 XD

CPU Core 之間溝通的時間成本

Hacker News 上看到「Measuring CPU core-to-core latency (github.com/nviennot)」這篇,專案在「Measuring CPU core-to-core latency」這裡,看起來是個有趣的研究,測試許多不同 CPU 內,跨 core 之間溝通的時間花費。

依照專案的說明,測試的方式是利用 cache coherence 來來量測:

We measure the latency it takes for a CPU to send a message to another CPU via its cache coherence protocol.

By pinning two threads on two different CPU cores, we can get them to do a bunch of compare-exchange operation, and measure the latency.

裡面已經測了很多不同的 CPU,然後可以看到一些有趣的結果。

像是第一張圖片的「Intel Core i9-12900K @ 8+8 Cores (Alder Lake, 12th gen) 2021-Q4」這組,大家還蠻好奇 CPU #8 到底是怎麼一回事,跨 core 溝通的 latency 特別低,還特別找了 CPU 的 die 圖片看看:

另外一個是 AWS 上的 c6a.metal,機種是「AMD EPYC 7R13 @ 48 Cores (Milan, 3rd gen) 2021-Q1」,可以看到被分成了六個區塊:

接下來在 ARM 平台,在更多 CPU core 的 c7g.16xlarge 上,機種「AWS Graviton3 @ 64 Cores (Arm Neoverse, 3rd gen) 2021-Q4」,會看到更多不平均的現象:

早一點的 c6gd.metal 雖然也還是 ARM 的 64 cores 機種「AWS Graviton2 @ 64 Cores (Arm Neoverse, 2nd gen) 2020-Q1」,但可以看到很不一樣的 latency pattern:

大致上可以感覺到當 core 數愈多就會有很多技術上的瓶頸,導致不同 core 之間的溝通成本不一樣... 這個感覺跟當初學到 NUMA 的情況有點像。

Multithreading 版本 pt-online-schema-change

看起來是個嘗試,Percona 的人試著修改 pt-online-schema-change,讓他可以在 INSERT 時 multi-threading,然後看效果:「Multithreaded ALTER TABLE with pt-online-schema-change and myloader」。

可以看出來 thread 夠多的情況下其實都變快不少 (上圖主要是看絕對數字,下圖是看相對比率):

如果沒有意外的話應該會有更多的測試,而這些測試沒問題的話,之後的官方版本裡面應該就會有這個功能。

AMD Ryzen Threadripper 3990X 在 Windows 上的效能

John Carmack 注意到在 AMD Ryzen Threadripper 3990X 上因為 Windows 的 group limit 限制而造成效能問題:

但這點可以透過打散到兩個 group 改善 (workaround) 而提昇速度:

然後順便看了一下目前 CPU Benchmark 網站上對於高階 CPU 的跑分數據「PassMark - CPU Mark High End CPUs)」,可以看到 AMD 最近真是香噴噴的,用 3950X (16C/32T,105W) 殺 Intel 目前最高分的 W-3275M (28C/56T,205W),然後那個價差:

Intel 的 14nm 牙膏繼續擠...

用 Amazon SES 發 Trac 通知信的問題

Trac 在發 ticket 的通知信時,會定義自己的 Message-ID,另外後續變更的通知信件會增加 References 欄位,讓 mail client 可以配對起來 (變成一個 thread)。

Amazon SES 會把原來的 Message-ID 改掉,使用自己的 Message-ID 欄位,可是 References 欄位仍然維持不變... 這就導致 mail client 無法將第一封信 (只有被改過的 Message-ID) 與後續的信件 (References 所指到的信件不存在) 配對起來,只剩下後續的信件因為有相同的 References,所以 mail client 可以正確的配對起來。

所以我就決定生一個 workaround plugin,只要是沒有 References 的信件 (像是每張 ticket 的第一封信),就從 Message-ID 複製一份到 References 裡,這樣就可以讓後續的通知信件與第一封也連結起來了。另外評估這個 workaround 的副作用應該還好,所以就不判斷是不是 ticket 的通知信了...

這就是 trac-references-mail-decorator 這個套件的由來...

Microsoft 的 TTD 與 Mozilla 的 RR

也是個在瀏覽器 tab 上放了一陣子的連結... 先前看到 MicrosoftTime Travel Debugger (TTD),可以錄下程式執行的狀態,然後回放與搜尋:「Thoughts On Microsoft's Time-Travel Debugger」,另外有 CppCon 2017 上的影片,在 YouTube 上:

另外 Mozilla 也有類似的工具,叫做 rr (在影片開頭就有人問類似的問題 XD),程式碼在 GitHub 上:「mozilla/rr」。

而 TTD 與 rr 兩者最大的差異當然是平台支援的情況:

The most important and obvious difference between TTD and rr is that TTD is for Windows and rr is for Linux (though a few crazy people have had success debugging Windows applications in Wine under rr).

但另外一個也很重要的差異是 TTD 支援完整的 multi-threading,這對於現代的程式來說還蠻常見的:

TTD supports recording of multiple threads in parallel, while rr is limited to a single core.

當然,更完整的錄影也是要付出效能代價的:

On the other hand, per-thread recording overhead seems to be much higher in TTD than in rr. It's hard to make a direct comparison, but a simple "start Firefox, display mozilla.org, shut down" test run on similar hardware takes about 250 seconds under TTD and 26 seconds under rr.

不過有需要的時候應該會很方便?工具總是愈多愈好...

小台機器上的 innodb_purge_threads 對效能的影響

雖然「MyISAM, small servers and sysbench at low concurrency」這篇標題是在講 MySQL 上的 MyISAM,但還是有提到一些 InnoDB 的東西...

其中提到了 innodb_purge_threads 對效能的影響:

the default value for innodb_purge_threads, which is 4, can cause too much mutex contention and a loss in QPS on small servers. For sysbench update-only I lose 25% of updates/second with 5.7.17 and 15% with 8.0.1 when going from innodb_purge_threads=1 to =4.

當機器不大的時候,innodb_purge_threads 對於效能帶來的影響其實頗大的?

另外從作者最近的一系列測試看起來,5.7 在小機器的效能比 5.6 差不少... 這點在考慮 RDS 的時候也許要注意 (因為 t2.* 應該不算大 XD)。

MySQL 總算要拔掉 mysql_query_cache 了

半官方的 MySQL blog 上宣佈了拔掉 mysql_query_cache 的計畫:「MySQL 8.0: Retiring Support for the Query Cache」。

作者開頭引用了 ProxySQL 的人對 MySQL Query Cache 的說明:

Although MySQL Query Cache was meant to improve performance, it has serious scalability issues and it can easily become a severe bottleneck.

主要問題在於 MySQL Query Cache 在多 CPU 環境下很難 scale,很容易造成一堆 thread 在搶 lock。而且作者也同意 ProxySQL 的說法,將 cache 放到 client 的效能比較好:

We also agree with Rene’s conclusion, that caching provides the greatest benefit when it is moved closer to the client:

可以看到 Query Cache 在複雜的環境下對效能極傷。而之前也提到過類似的事情了:「Percona 對 mysql_query_cache 的測試 (以 Magento 為例)」、「關閉 MySQL 的 Query Cache」。

一般如果要 cache 的話,透過 InnoDB 裡良好的 index 應該還可以撐不少量起來。