- PostgreSQL genişletme proxy’si PgDog, SQL ayrıştırma performansını artırmak için Protobuf serileştirme yerine Rust doğrudan bağlama yaklaşımını benimsedi
- Mevcut Protobuf tabanlı yapı, C–Rust doğrudan dönüşümü (bindgen + Claude tarafından üretilen sarmalayıcı) ile değiştirilerek ayrıştırmada 5,45 kat, yeniden üretmede 9,64 kat hız artışı sağlandı
- Performans darboğazı pg_query_parse_protobuf fonksiyonunda tespit edildi; önbellekleme denemelerinden sonra köklü iyileştirme için mimari değişiklik yapıldı
- Claude LLM kullanılarak 6.000 satırlık Rust–C dönüşüm kodu otomatik üretildi ve
parse, deparse, fingerprint, scan gibi temel fonksiyonlara uygulandı
- Bu optimizasyonla PgDog’un CPU kullanımı ve gecikmesi azaldı, PostgreSQL yatay ölçeklendirme proxy’si olarak verimliliği belirgin biçimde arttı
PgDog ve Protobuf’un sınırları
- PgDog, PostgreSQL’i ölçeklendirmek için kullanılan bir proxy ve SQL sorgularını ayrıştırmak için içeride libpg_query kullanıyor
- Rust ile yazılmış durumda ve daha önce C kütüphanesiyle iletişimde Protobuf serileştirme/ters serileştirme kullanıyordu
- Protobuf hızlı olsa da doğrudan bağlama yaklaşımı daha hızlı
- PgDog ekibi
pg_query.rs’yi fork’layarak Protobuf’u kaldırdı ve C–Rust doğrudan bağlama uyguladı
- Sonuç olarak sorgu ayrıştırma 5,45 kat, yeniden üretme ise 9,64 kat hızlandı
Benchmark sonuçları
- Benchmark’lar PgDog’un fork deposunda yeniden üretilebilir
pg_query::parse (Protobuf): 613 QPS
pg_query::parse_raw (doğrudan C–Rust): 3357 QPS
pg_query::deparse (Protobuf): 759 QPS
pg_query::deparse_raw (doğrudan Rust–C): 7319 QPS
Performans darboğazı analizi ve önbellekleme denemesi
- CPU zaman kullanımını samply profilleyicisiyle inceleyen ekip, darboğazın pg_query_parse_protobuf fonksiyonu olduğunu doğruladı
- Önbellekleme ile kısmi iyileştirme denenmişti
- Sorgu metnini anahtar, AST’yi değer olarak tutan LRU algoritmalı hash map önbelleği kullanıldı
- Hazırlanmış ifadeler kullanıldığında yeniden kullanım mümkün oluyordu
- Ancak bazı ORM’ler binlerce benzersiz sorgu üretiyor ve eski PostgreSQL sürücüleri hazırlanmış ifadeleri desteklemiyordu; bu yüzden önbellek verimi düşük kaldı
LLM ile Protobuf’u kaldırmak
- PgDog ekibi, Protobuf’u ortadan kaldıran Rust bağlamalarını üretmek için Claude LLM kullandı
- Yapay zeka, kapsamı net ve doğrulanabilir görevlerde etkili biçimde çalıştı
- Claude,
libpg_query’nin Protobuf spesifikasyonunu temel alarak C yapılarını Rust yapılarına eşledi
- İki günlük yinelemeli çalışmanın sonunda 6.000 satırlık özyinelemeli Rust kodu tamamlandı
parse, deparse, fingerprint, scan fonksiyonlarında uygulanarak pgbench’e göre %25 performans artışı doğrulandı
Uygulamanın teknik yapısı
- Rust ile C arasındaki dönüşümde, yapıları doğrudan eşlemek için unsafe fonksiyonlar kullanıldı
- C yapıları PostgreSQL API’sine aktarılıp AST oluşturuluyor, ardından Rust tarafına özyinelemeli dönüşüm yapılıyor
- Her AST düğümü convert_node fonksiyonuyla işleniyor ve SQL sözdizimindeki yüzlerce token eşleniyor
- SELECT, INSERT gibi her düğüm türü için ayrı dönüşüm fonksiyonları bulunuyor
- Dönüşüm sonucu, mevcut Protobuf yapıları (
protobuf::ParseResult) yeniden kullanılarak testlerde bayt düzeyinde karşılaştırmalı doğrulama yapılabiliyor
- Özyinelemeli algoritma, daha az bellek ayırdığı ve CPU önbelleğini daha verimli kullandığı için yineleme tabanlı uygulamadan daha hızlı
- Yineleme tabanlı yaklaşım, gereksiz bellek ayırmaları ve hash map aramaları nedeniyle daha yavaş kaldı
Sonuç
- Postgres ayrıştırıcısının ek yükü azaltılarak PgDog’un gecikme, bellek ve CPU kullanımı birlikte düşürüldü
- Bu optimizasyonla PgDog, daha hızlı ve daha düşük maliyetle işletilebilen bir PostgreSQL ölçeklendirme proxy’si haline geldi
- PgDog, PostgreSQL’in yatay ölçeklendirmedeki sonraki adımını birlikte inşa edecek mühendisler arıyor
3 yorum
Belki de asıl metni yanlış yorumluyorumdur ama özellikle Rust’la ilgili yazılar, özden sapıp sanki "Rust olduğu için" hızlanmış gibi yazılıyor gibi geliyor bana.
Bu yazının asıl noktası ise gereksiz serileştirme ek yükünün azaltılmasıyla performansın iyileşmiş olması.
Şimdi tekrar bakınca bunun da Rust’ı öven bir yazı olmadığını düşünüyorum ama acaba diğer yazılar yüzünden bende olumsuz bir algı mı oluştu?
Ben de orijinal başlığın, gerçek içerikten farklı olarak fazlasıyla Rust odaklı olduğu ve sanki performans artışına odaklanıyormuş gibi göründüğü için, onu biraz düzelttim.
Rust yazılarında bu eğilim sık sık görüldüğünden, biraz filtre uygulayarak okumak gerekiyor gibi görünüyor.
Hacker News görüşleri
Başlık sanki Rust 5 kat performans artışı sağlamış gibi görünüyor, ama aslında ironik olan şey bunun başlangıçta yavaşlamaya yol açmış olması
Sorun, Rust ile yazılmış yazılımın C ile yazılmış
libpg_querykullanmak zorunda olmasıydı; ancak doğrudan bağlanamadığı için Protobuf tabanlı Rust–C binding kullanılmışBu yaklaşım yavaştı; sonuç olarak LLM yardımıyla taşınabilir olmayan ama çok daha iyi optimize edilmiş yeni bir binding yazılmış
Eğer en başta C ile yazılmış olsaydı dönüşüm sürecine gerek kalmayacaktı. Yani başlık için daha doğru ifade “Rust kullanımından kaynaklanan performans kaybını azalttı” olurdu
Dönüşüm katmanları taşınabilirlik ve güvenlik sağlar, ama sonuçta kopyalama·dönüştürme·serileştirme tekrarlandıkça uygulamayı yavaşlatan nedenlerden biri haline gelir diye düşünüyorum
Rust’tan C kütüphanesi çağırmak çok kolaydır ve güvenli sarmalayıcılar da zaten bolca vardır
Araya Protobuf koyan bir yapı neredeyse hiç görmedim; darboğaz da buydu
Başlık, tıklama çekmek için kullanılan “Rust ile yeniden yazdı” tarzı bir meme’e daha yakın görünüyor
Asıl kütüphane zaten serileştirme/deserileştirme tekrarına dayanan hatalı bir tasarıma sahipti; asıl mesele bunun kaldırılması
Başlık için daha doğru ifade “Protobuf’u normal bir API ile değiştirince 5 kat hızlandı” olurdu
Rust’ta C binding en kolay işlerden biridir ve API çok büyük değilse basittir
Protobuf’un bellek içi veri alışverişi için uygunsuz bir araç olduğunu düşünüyorum
Bundan sonra LLM sayesinde farklı dillere port etme işlerinde patlama görebiliriz gibi geliyor
Başlık biraz yanıltıcı
Esasen olay “Protobuf serileştirme adımını kaldırınca hızlandı”
İstemci ve sunucu bağımsız güncellense bile çalışmaya devam etmesini sağlar ve farklı diller arasında iletişimi kolaylaştırır
Büyük ölçekli sistemlerde bu tür esneklik çok önemlidir
memcpyya dammapçok daha hızlıdır, ama Rust ekosistemi bu tür güvensiz yöntemlere mesafeli dururSorun Rust değil; Protobuf’un genelleştirilmiş bir depolama formatı gibi kullanılmasının yavaşlığa neden olmuş olması muhtemel
Sonuçta asıl mesele bunu belirli bir amaca göre sadeleştirmekti
Başlığa Rust koymak tıklama almak için yapılmış bir seçim gibi duruyor
pg_query’nin asıl yazarı arka planı açıklıyorBaşlangıçta pganalyze içinde Postgres sorgularını parse edip tablo referanslarını bulmak, sorguları yeniden yazmak ve formatlamak için kullanılmış
İlk başta JSON kullanılmış, sonra ise birçok dilde (Ruby, Go, Rust, Python vb.) tip güvenli binding’leri daha kolay sunabilmek için Protobuf’a geçilmiş
Rust gibi diller için FFI daha iyi, ama diğer dillerde bakım yükü daha fazla
Lev’in girişimini destekliyorlar ve ileride libpg_query’ye doğrudan FFI ile erişilebilecek fonksiyonlar eklemeyi planlıyorlar
Yine de performansın kritik olmadığı durumlarda Protobuf hâlâ daha pratik bir tercih
“5 kat daha hızlı” sözü, Cap’n Proto’nun “sonsuz derecede hızlı” şakasını hatırlatıyor
Başlık abartılı ama yapılan iş etkileyici
Protobuf tamamen kaldırılmış değil; kullanım biçimi optimize edilmiş
“X’e geçince 5 kat hızlandı” ifadesi çoğu zaman “berbat olan implementasyonu düzelttik” anlamına gelir
Buradaki temel dersler şunlar
Rust FFI’da da bir miktar overhead vardır; yani asıl kazanım dilden değil, veri akışını yeniden tasarlama ve optimizasyon çabasından geliyor
FlatBuffers daha hızlı olabilir, ama Protobuf’un tercih edilme nedeni büyük şirketler tarafından bakılıyor olması
Sonuç olarak “Google yaptıysa güvenlidir” algısının pek temeli yok
code.google.com) kod koyup sonra bunun çöküşünü yaşamıştımSadece bellek paylaşımı ve sürüm alanları olan bir zero-copy yapı yeterliyse, ille de Protobuf kullanmak için bir neden görmüyorum
Protobuf performansının şaka seviyesinde olduğunu düşünüyorum
Serileştirmenin fiilen bedava olduğu zero-copy formatlar kullanılmalı
Örneğin benim yaptığım Lite³, FlatBuffers’tan 242 kat daha hızlı
Protobuf’un kullanılmasının nedeni ekosistem, şema, dil bazlı araçlar gibi birçok gerçek dünyadaki etken
Aslında sorun Rust ya da Protobuf değil; PostgreSQL soyutlama katmanındaki verimsiz serileştirme implementasyonuydu
pgdogbu katmanı kaldırıp verileri doğrudan C API üzerinden aktardıGereksiz özellikleri çıkarınca doğal olarak hızlanıyor
Ama bazı insanlar için serileştirme hâlâ gerekli olabilir
Bu kişilere “Rust’a geçin” diyen bir başlık yanlış mesaj verir
Sonuçta çoğu durumda JSON yeterlidir; gerçekten daha fazla hız gerekiyorsa serileştirmenin kendisinden kaçınmak gerekir
Bu adil olmayan bir karşılaştırma
IPC iletişiminde serileştirme protokolü kullanmak doğal olarak overhead yaratır
“%20 hızlanıyorsa iyileştirmedir, 10 kat hızlanıyorsa en baştan yanlış yapılmıştır” sözü tam da buna uyuyor