Java 21 的 ZGC 在 Netflix 的效果

Hacker News 上看到連結「Bending pause times to your will with Generational ZGC (netflixtechblog.com)」,發現這篇還沒整理:「Bending pause times to your will with Generational ZGC」,裡面講的東西都有圖有數字 (i.e. Y 軸),作者是 Danny Thomas

在這之前他們就已經知道 GC pause 是延遲的重要來源,會導致 timeout & retry:

In both our GRPC and DGS Framework services, GC pauses are a significant source of tail latencies.

That’s particularly true of our GRPC clients and servers, where request cancellations due to timeouts interact with reliability features such as retries, hedging and fallbacks.

第一張圖拉出來的資料是 error rate,白色是上個禮拜的資料,紫色是這個禮拜的資料,而從 G1GC 切到 ZGC 是在 2023/11/16 發生的:

可以看到很明顯的 error rate 改變:尖峰從 2k 下降到大約 0.3k,大約是原來的 1/6 到 1/7 的下降。

第二張圖是 GC 的時間:

可以看到 G1GC 還是偶而會撞到 2 秒,發生時平均值也都還是會 >100ms,切到 ZGC 後直接降到個位數 ms 等級了。

第三張圖是 memory overhead 的部分:

從圖上可以看到上週與本週的對比,導入 ZGC 後記憶體的使用量下降了,不過文裡面倒是沒解釋這點,反而提到 ZGC 比起 G1GC 有個固定的 3% overhead:

ZGC has a fixed overhead 3% of the heap size, requiring more native memory than G1. Except in a couple of cases, there’s been no need to lower the maximum heap size to allow for more headroom, and those were services with greater than average native memory needs.

第四張則是 Huge Pages 的差異,這邊要注意這張圖的 Y 軸不是從 0 計算:

可以看到在開 Huge Pages 後,在 RPS (request per second) 不變的情況下 CPU 使用率是有下降的,大約從 50% 降到 45% 左右,不過這張圖的時間跨度有點少,應該是要拉長一點的圖... 不過既然被提出來了,就假設 Netflix 內看起來應該是有這個趨勢,只是抓圖的時候懶了點?

整體算是大成功?

sudo 對 ROWHAMMER 攻擊的 mitigation

看到「Rowhammer Resistant Coding in Sudo (github.com/sudo-project)」這邊的討論,提到了 sudo 專案 (變成 root 那個 sudo 軟體) 怎麼緩解 ROWHAMMER 攻擊的實作,原連結是去年九月時 GitHub 上的 commit:「Try to make sudo less vulnerable to ROWHAMMER attacks.」。

從 commit 裡面可以看到這個:

- #define AUTH_SUCCESS		0
- #define AUTH_FAILURE		1
- #define AUTH_INTR		2
- #define AUTH_ERROR		3
- #define AUTH_NONINTERACTIVE	4
+ #define AUTH_SUCCESS		0x52a2925	/* 0101001010100010100100100101 */
+ #define AUTH_FAILURE		0xad5d6da	/* 1010110101011101011011011010 */
+ #define AUTH_INTR		0x69d61fc8	/* 1101001110101100001111111001000 */
+ #define AUTH_ERROR		0x1629e037	/* 0010110001010011110000000110111 */
+ #define AUTH_NONINTERACTIVE	0x1fc8d3ac	/* 11111110010001101001110101100 */

可以看到想法上是讓要攻擊時需要改變的 bit 數量大幅增加?尤其是對 AUTH_SUCCESShamming distance

另外這段也是類似的設計:

+ /* Allowed by policy (rowhammer resistent). */
+ #undef ALLOW
+ #define ALLOW	 0x52a2925	/* 0101001010100010100100100101 */
+ 
+ /* Denied by policy (rowhammer resistent). */
+ #undef DENY
+ #define DENY	 0xad5d6da	/* 1010110101011101011011011010 */

這邊 ALLOWDENY 這邊也是把 hamming distance 設計到最大,兩個 XOR 後剛好是 0xffffffff

會不會變成安全相關軟體實作上的 practice?

AWS 推出 32TB RAM 的機種 u7in-32tb.224xlarge

最近是 AWS re:Invent 2023,會有蠻多消息陸陸續續冒出來。

剛剛看到「Introducing Amazon EC2 high memory U7i Instances for large in-memory databases (preview)」這個,AWS 推出了 u7in-32tb.224xlarge,896 vCPU + 32TB RAM 的機器。

先前最大的應該是 u-24tb1.112xlarge,448 vCPU + 24TB RAM 的機器 (包括 vCPU 數量與記憶體大小),所以這次 vCPU 數量多一倍,記憶體多 50%。

很理所當然的又拿 SAP 來介紹了:

The new U7i instances are designed to support large, in-memory databases including SAP HANA, Oracle, and SQL Server.

比較特別的是 us-east-1 沒有在第一波的名單,反而是 us-west-2 以及其他的區域,不確定當初怎麼決定的:

Powered by custom fourth generation Intel Xeon Scalable Processors (Sapphire Rapids), the instances are now available in multiple AWS regions in preview form, in the US West (Oregon), Asia Pacific (Seoul), and Europe (Frankfurt) AWS Regions, as follows[.]

因為是 preview 階段,可能是這些區域有大客戶有需求,所以先佈到這幾區?

Sentry 的替代品:GlitchTip

GlitchTipSentry 的替代品,你仍然可以用 Sentry SDK 發送錯誤訊息,但接收端則是換成 GlitchTip。

Sentry 本身已經是 open source software 了 (可以在 getsentry/self-hosted 這邊裝) (參考「Sentry 的 License 不是 Open Source License...」),但最近的版本愈來愈肥,8GB RAM + 512MB Swap 的機器,光跑起來就蠻吃緊的。

對於在 AWS 的人來說,這等於被迫要用 16GB RAM 的機器跑 Sentry,像是 r6a.large (在 us-east-1 為 $0.1134/hr,約 $81.6/mo),如果是一年 RI 的話可以六折,三年 RI 可以四折,是有點費用,但這個費用對於公司用戶來說通常沒有太大問題。

GCP 的話也類似,但可以開 10GB RAM 來用,真的有用到的時候再逐步拉高記憶體空間,不過成本結構跟 AWS 是差不多的,所以對於公司也不是太大問題。

但對個人來說,光一個 Sentry 一個月就得花個 $80/mo 實在是太貴,而且考慮到這兩年的 Sentry 愈來愈沒有在管記憶體用量了:從之前 4GB RAM 能跑,到現在 8GB RAM 光開機就沒有 free memory,可以想像之後應該會愈來愈肥,所以試著找了替代方案。

目前看到的替代方案是 GlitchTip,在 server 端吃同樣的 input,所以 client 端可以維持原來的 sentry sdk,可以用雲端服務,也可以自己架設:「GlitchTip Installation Guide」。

跑起來後大約吃 500MB RAM,基本的功能都有,包括基本的 event group & call stack information,以及常用的 integration (像是 E-mail 通知與 Slack 通知),但畫面上沒有 Sentry 那麼好看,不過還算是乾淨。

有些功能要找文件 trial and error,跟 Sentry 文件相比起來完整度差一點,但還行...。

目前看起來自己用可以用這個,Sentry 真的太胖了...

Cloudflare Workers 推出新的計費模式:以 CPU time 收費

CloudflareWorkers 這個產品 (serverless 產品線) 推出了以 CPU time 收費的模式:「New Workers pricing — never pay to wait on I/O again」。

在這之前大家都是以 wall time 在計費,但這對於會卡在 I/O 很久的應用來說很不利,這次 Cloudflare 提出方案改用 CPU time 來計費,的確有吸引到我的目光...

這是舊的:

這是新的:

就這兩張比較起來有個不是很確定的部分,現在不看 memory 用量收費了?Pricing 這頁裡面的資量已經把 「Standard」更新上去了,但好像還是沒提到 memory?

不過不是馬上生效,而是這個月的月底 2023/10/31 可以選擇切到新的方案:

Starting October 31, 2023, you will have the option to opt in individual Workers and Pages Functions projects on your account to new pricing, and newly created projects will default to new pricing. You’ll be able to estimate how much new pricing will cost in the Cloudflare dashboard. For the majority of current applications, new pricing is the same or less expensive than the previous Bundled and Unbound pricing plans.

另外明年 2024/03/01 會全部強制切到新的方案:

If you’re on our Workers Paid plan, you will have until March 1, 2024 to switch to the new pricing on your own, after which all of your projects will be automatically migrated to new pricing. If you’re an Enterprise customer, any contract renewals after March 1, 2024, will use the new pricing.

用 CPU time 的確是好不少,但不知道這個 billing 的方式沒有其他地雷...

Ruby 3.3 的速度再次提升

在「Ruby 3.3's YJIT Runs Shopify's Production Code 15% Faster」這篇提到了 Ruby 3.3 的速度再次提升的消息。

Shopify 上面的測試,3.3.0-preview2 的速度已經比 3.2.2 (兩者都有開啟 YJIT) 快了 13%,而且 p50/p90/p99 都有對應的改善:

不過有提到一些要注意的點,像是記憶體的用量又會再更高 (本來開 YJIT 的時候就已經有增加了),如果是對記憶體比較敏感的環境,會需要注意這點:

Since Ruby 3.3.0-preview2 YJIT generates more code than Ruby 3.2.2 YJIT, this can result in YJIT having a higher memory overlead. We put a lot of effort into making metadata more space-efficient, but it still uses more memory than Ruby 3.2.2 YJIT. We’re looking into skipping compilation of paths that are less frequently executed.

但 server 端應該是還好 (記憶體給多一點),整體是個可以期待的方向...

Alpaca.cpp 有 13B 與 30B 的 model 可以玩了

在「Stanford Alpaca 與 Alpaca.cpp」這邊有提到 Alpaca 有放出 7B 的 model 可以用,後來 community 也照著同樣的方式訓練出對應的 13B 與 30B 的 model 了,在 Alpaca.cpp 專案的 README.md 有寫怎麼取得。

看起來比較大的限制是記憶體,13B 與 30B 的記憶體建議是 >10GB RAM 與 >32GB RAM,不知道剛好 32GB RAM 跑不跑得動,先拿 32GB RAM 的機器跑看看,如果真的不行的話家裡桌機是 64GB RAM,應該是不會有大問題...

不過 13B 的 model 檔案位置是放在 IPFS 上,透過這幾個 gateway 下載起來都有點慢,有需要的人用 BitTorrent 可能會快一點?

Update:測了一下 13B 的版本:

> Explain FAANG.
FAANG refers to five of America's largest technology companies - Facebook, Apple Inc., Amazon, Netflix and Google (Alphabet). These are some of the most valuable brands in today’s marketplace with a combined value that exceeded $3 trillion as at 2018.

以及 30B 的版本:

> Explain FAANG.
FAANG stands for Facebook, Amazon, Apple, Netflix and Google - five of the most powerful technology companies in today's world. These tech giants have become increasingly influential over recent years due to their dominance in various markets such as social media platforms (Facebook), e-commerce websites (Amazon) or streaming services (Netflix).

AWS 推出了 Graviton3 的機種

Amazon EC2 推出了 Graviton3 的機種:「New Graviton3-Based General Purpose (m7g) and Memory-Optimized (r7g) Amazon EC2 Instances」。

第一波只有一般的 m7g 與記憶體型的 r7g,而計算型的 c7g 大家在 Twitter 上猜應該晚點會放出消息。在去年五月就推出了:「AWS 推出 c7g 機種」。

目前只在歐美的 us-east-1us-east-2us-west-2eu-west-1 區提供,亞洲目前都還沒有這些機種可以用:

M7g and R7g instances are available today in the US East (N. Virginia), US East (Ohio), US West (Oregon), and Europe (Ireland) AWS Regions in On-Demand, Spot, Reserved Instance, and Savings Plan form.

官方宣稱比 Graviton2 的 m6g & r6g 多了 25% 的效能,不過我另外查了一下 us-east-1 上的價錢,也貴了 6% 左右,如果依照官方宣稱的數字計算,大約是 18% 左右的 CP 值提昇,對於有實際上跑滿的 CPU 的人是個不錯的效能提昇:

Today I am happy to tell you about the newest Amazon EC2 instance types, the M7g and the R7g. Both types are powered by the latest generation AWS Graviton3 processors, and are designed to deliver up to 25% better performance than the equivalent sixth-generation (M6g and R6g) instances, making them the best performers in EC2.

裡面有提到在 Graviton3 的一個架構上的大改變是記憶體從 DDR4 變到 DDR5,這使得記憶體的傳輸頻寬提昇了 50%:

Both types of instances are equipped with DDR5 memory, which provides up to 50% higher memory bandwidth than the DDR4 memory used in previous generations.

接下來是看有沒有下放到 t 系列的計畫,像是 t5g 之類的,有的話再用看看好了,不過 blog 這台已經買了三年 RI,等到期間滿了之後說不定都有 Graviton4 或是 Graviton5 了...

Windows 11 瘦身版本的 Tiny11

Tiny11NTDEV 弄出來的精簡版 Windows 11:「De-Bloated Windows 11 Build Runs on 2GB of RAM」。HN 上對應的討論在「De-Bloated Windows 11 Build Runs on 2GB of RAM (tomshardware.com)」。

It just uses around 8GB of space compared to the 20+GB that a standard installation does.

但有些限制,像是安全性更新需要自己來:

This OS install “is not serviceable,” notes NTDev. “.NET, drivers and security definition updates can still be installed from Windows Update,” so this isn’t an install which you can set and forget.

另外像是透過 WinSxS 安裝的功能 (包括語言) 會無法安裝:

Moreover, removing the Windows Component Store (WinSxS), which is responsible for a fair degree of Tiny11’s compactness, means that installing new features or languages isn’t possible.

但我記得拔掉 WinSxS 應該會影響蠻多東西的?這樣的系統應該是拿來跑跑 CI 或是固定用途還行,一般性的用途不知道會卡多少東西...

另外除了使用的磁碟空間變小以外,記憶體的使用量也大幅下降,畢竟也拔掉了一堆肥大的軟體:

In testing, NTDev said that Tiny11 could “run great” on a system with just 2GB of RAM.

calloc() 與 malloc() 的差異

前陣子在 Hacker News Daily 上看到的,原文是 2016 的文章:「Why does calloc exist?」,裡面講的東西包括了 implementation dependent 的項目,所以要注意一下他的結論未必適用於所有的平台與情境。

malloc()calloc() 的用法是這樣,其中 calloc() 會申請 countsize 的空間:

void* buffer1 = malloc(size);
void* buffer2 = calloc(count, size);

第一個差異是,count * size 可能會 overflow (而 integer overflow 在 C 裡面是 undefined behavior),這點除非你在乘法時有檢查,不然大多數的行為都還是會生一個值出來。

calloc() 則是會幫你檢查,如果會發生 overflow 的時候就不會真的去要一塊記憶體用。

第二個差異是 calloc() 保證會將內容都設定為 0,這點在 POSIX 的標準裡面是這樣寫的:

The calloc() function shall allocate unused space for an array of nelem elements each of whose size in bytes is elsize. The space shall be initialized to all bits 0.

但作者就發現 malloc() + memset() + free() 還是比 calloc() + free() 慢很多:

~$ gcc calloc-1GiB-demo.c -o calloc-1GiB-demo
~$ ./calloc-1GiB-demo
calloc+free 1 GiB: 3.44 ms
malloc+memset+free 1 GiB: 365.00 ms

研究發現是 calloc() 用了 copy-on-write 的技巧,先把所有的 page 都指到同一塊完全被塞 0 的記憶體,只有在真的寫到該段記憶體時,系統才會要一塊空間來用:

Instead, it fakes it, using virtual memory: it takes a single 4 KiB page of memory that is already full of zeros (which it keeps around for just this purpose), and maps 1 GiB / 4 KiB = 262144 copy-on-write copies of it into our process's address space. So the first time we actually write to each of those 262144 pages, then at that point the kernel has to go and find a real page of RAM, write zeros to it, and then quickly swap it in place of the "virtual" page that was there before. But this happens lazily, on a page-by-page basis.

但畢竟這是 implementation dependent,看看有個印象就好。