3 poin oleh GN⁺ 2024-03-18 | 1 komentar | Bagikan ke WhatsApp
  • Fedora dan Ubuntu 24.04 LTS kembali membangun komponen OS seperti libc dengan frame pointer diaktifkan secara default, sehingga profiling Linux dan Flame Graph dapat melihat stack yang lebih lengkap
  • Tanpa frame pointer, profiler dapat menghentikan stack walking di lapisan library seperti libc atau mengikuti frame yang salah, sehingga CPU dan Off-CPU Flame Graph bisa rusak
  • Perubahan gcc pada i386 tahun 2004 adalah pilihan untuk mendapatkan satu register tambahan, tetapi meluas hingga x86-64 dan meninggalkan biaya jangka panjang bagi profiler sistem dan observabilitas berbasis eBPF
  • Dari pengalaman penerapan pada Java dan libc di Netflix, biayanya biasanya kurang dari 1%, meski beberapa laporan menunjukkan 1~2%, dan pada microbenchmark khusus atau workload yang tidak lazim bisa mencapai 10%
  • Ada alternatif seperti LBR, DWARF, eBPF stack walker, ORC, SFrames, dan Shadow Stacks, tetapi cara paling praktis saat ini untuk mencari peningkatan performa di production adalah mengaktifkan frame pointer secara default

Mengapa Flame Graph rusak

  • CPU Flame Graph bisa tampak normal dari luar, tetapi pada sistem yang libc-nya dikompilasi tanpa frame pointer, sebagian sampel bisa salah terkumpul di atas [unknown]
    • Pada contoh, 15% sampel di sisi kiri berada di lokasi yang salah dan frame aplikasi hilang
    • Profiler melewati frame kernel lalu mencapai syscall dan libc syscall wrapper, kemudian gagal menyelesaikan simbol frame berikutnya
  • Penyebabnya adalah optimisasi compiler yang memakai register frame pointer bukan sebagai titik acuan stack frame melainkan untuk menyimpan data
    • Profiler tidak tahu apakah nilai tersebut hanya angka biasa, sehingga mencoba menafsirkannya sebagai alamat fungsi
    • Jika nilainya tidak menunjuk ke frame berikutnya, stack walking akan berhenti
    • Jika kebetulan tampak seperti pointer yang valid, junk frame yang salah bisa muncul
    • Jika nilainya menunjuk ke dirinya sendiri, tumpukan junk frame bisa terbentuk hingga mencapai batas maksimum frame perf
  • Off-CPU Flame Graph dapat banyak memunculkan fungsi read/write dan mutex dari libc, sehingga kerusakannya lebih parah saat tidak ada frame pointer
  • Jika aplikasi itu sendiri juga dibangun tanpa frame pointer, bukan hanya library, seluruh pelacakan stack bisa ikut terganggu

Informasi yang diberikan frame pointer

  • ABI x86-64 mendefinisikan bahwa %rbp dapat digunakan sebagai base pointer dari stack frame, yaitu frame pointer
  • Profiler dan debugger eksternal seperti Linux perf dan eBPF memakai informasi ini untuk melakukan pelacakan stack, lalu memvisualisasikan hasilnya sebagai Flame Graph
  • Ada catatan kaki pada ABI x86-64 bahwa penggunaan %rbp bersifat opsional
    • Dengan mengindeks stack frame memakai %rsp, dua instruksi dapat dihemat pada prologue dan epilogue
    • %rbp dapat dipakai sebagai register tujuan umum tambahan

Penghapusan pada 2004 dan dampak 20 tahunnya

  • Pada 2004, gcc diubah agar pada backend i386 secara default berperilaku setara dengan -fomit-frame-pointer -ffixed-ebp
  • i386 hanya memiliki 4 register tujuan umum, sehingga melepas %ebp secara signifikan menambah register yang tersedia
    • Salah satu alasan perubahan ini adalah ingin memberikan performa yang lebih baik daripada compiler icc milik Intel
    • Saat itu juga dinilai debugger tidak akan rusak karena mendukung teknik stack walking lain
  • Perubahan ini lalu diterapkan juga ke x86-64
    • x86-64 memiliki lebih dari 12 register, sehingga manfaat mendapatkan satu register tambahan tidak sebesar pada i386
    • Profiler sistem masa kini seperti eBPF belum ada saat itu, dan perubahan ini dapat merusaknya
  • Eric Schrock pada 2004 menilai bahwa manfaat mendapatkan register tujuan umum ke-17 pada amd64 tidak cukup besar dibanding hilangnya kemampuan debugging
    • Ia menilai bahwa begitu /usr/bin mulai dikompilasi tanpa frame pointer, keadaan akan lepas kendali
    • Di Linux, aliran yang sama menyebar bukan hanya ke /usr/bin, tetapi juga ke /usr/lib dan kode aplikasi

Pengalaman penerapan di Java, libc, dan Netflix

  • Saat bergabung ke Netflix pada 2014, seluruh stack aplikasi rusak karena Java belum memiliki dukungan frame pointer yang memadai
  • Perbaikan untuk JVM c2 compiler dibuat, lalu Oracle mengerjakannya ulang dan menambahkannya sebagai opsi -XX:+PreserveFramePointer di JDK8u60
  • Perubahan di Java memungkinkan ditemukannya berbagai peningkatan performa dalam kode aplikasi, tetapi libc masih merusak sebagian sampel CPU dan sebagian besar Off-CPU Flame Graph
  • Setelah itu, libc dengan frame pointer dikompilasi sendiri untuk production, dan bersama Canonical juga dikerjakan libc pra-build untuk Ubuntu
    • Untuk sementara waktu, penggunaan libc6-prof direkomendasikan
    • libc6-prof adalah libc6 yang menyertakan frame pointer

Overhead performa dan kasus pengecualian

  • Dalam penerapan di production, biaya menambahkan frame pointer ke libc dan Java biasanya kurang dari 1%
  • Ada aplikasi pengecualian yang menunjukkan overhead 10%
    • Itu adalah aplikasi tidak lazim yang membuat pelacakan stack lebih dari 1000 frame melalui Groovy
    • Linux perf juga tidak bisa menanganinya, sehingga Arnaldo Carvalho de Melo dari Red Hat menambahkan sysctl kernel.perf_event_max_stack untuk workload Netflix
    • Lingkungan tersebut adalah mesin virtual tanpa fitur profiling hardware tingkat rendah, jadi tidak bisa dipastikan apakah 10% itu seluruhnya disebabkan frame pointer
  • Laporan lain menunjukkan biaya sekitar 1% atau 2%
  • Microbenchmark bisa memburuk hingga 10%
    • Saat fungsi kecil dieksekusi berulang dalam loop, instruksi tambahan bisa memengaruhi kehangatan L1 cache atau batas cache line
    • Dalam kasus ini, bukan frame pointer itu sendiri, melainkan penambahan apa pun pada hot function dapat menimbulkan efek serupa
  • Benchmark Python scimark_sparse_mat_mult juga bisa mencapai 10%
    • Dalam analisis Andrii Nakryiko, ini adalah kasus khusus saat gcc pada fungsi besar memakai offset relatif %rbp alih-alih offset %rsp, sehingga membutuhkan lebih banyak byte dan menimbulkan masalah performa
    • Setelah itu, ada kabar bahwa Python telah diperbaiki agar bisa mengaktifkan kembali frame pointer sebagai default
  • Peningkatan performa yang ditemukan berkat frame pointer berkisar dari 5% hingga 500%, sehingga biaya yang biasanya di bawah 1% dianggap layak
  • Jika perangkat tidak memiliki kebutuhan profiling atau debugging, mengompilasi tanpa frame pointer tetap bisa diterima, tetapi sasaran utamanya adalah Enterprise Linux dan server backend

Aktivasi default di Fedora dan Ubuntu

  • Perusahaan besar seperti Meta, Google, dan Netflix sudah lebih dulu memakai libc dengan frame pointer untuk mendapatkan kemampuan profiling
  • Upaya pertama untuk mendorong aktivasi default ke upstream Fedora memicu perdebatan panjang
    • Diskusi Fedora menjadi thread dengan 116 postingan
    • Salah satu peserta meminta agar Meta atau Netflix menyediakan infrastruktur side repository untuk pengujian, benchmark, dan pengukuran ukuran kode
    • Jonathan Corbet merangkumnya dalam Fedora's tempest in a stack frame
  • Fedora kemudian meninjau ulang usulan itu dan menerimanya, menjadi distribusi pertama yang mengaktifkan kembali frame pointer
  • Ubuntu juga mengumumkan frame pointers by default di Ubuntu 24.04 LTS
  • Ada tambahan kabar bahwa Arch Linux juga sedang mengaktifkan frame pointer
  • Stack walking pada library OS membaik berkat perubahan ini, tetapi runtime aplikasi mungkin tetap memerlukan pengaturan terpisah
    • Java menyediakan opsi -XX:+PreserveFramePointer
    • Go telah menjadikan dukungan frame pointer sebagai default sejak beberapa tahun lalu

Kandidat stack walking setelah frame pointer

  • LBR(Last Branch Record): fitur hardware Intel dengan batas 16 atau 32 frame, tidak cukup untuk kebanyakan stack aplikasi tetapi dapat dipakai sebagai cara terakhir untuk memperoleh sebagian informasi stack
  • BTS(Branch Trace Store): fitur Intel yang lebih longgar soal batas kedalaman stack, tetapi memiliki biaya load/store memori dan penanganan interrupt buffer overflow BTS
  • AET(Architectural Event Trace): tracer berbasis JTAG yang dapat melacak event CPU, BIOS, dan device tingkat rendah, dan tampaknya juga bisa dipakai untuk stack trace, meski belum pernah digunakan secara langsung
  • DWARF: format debuginfo biner yang telah lama dipakai di debugger
    • Ada catatan bahwa pernah ada pengerjaan JIT-to-DWARF untuk runtime JIT
    • Ini tidak diharapkan praktis pada JVM Java di server production yang sibuk dan terus menjalankan c2
    • Biaya stack walking DWARF sendiri juga tinggi
  • eBPF stack walking: tracer eksternal dapat berjalan di dalam runtime seperti JVM tanpa dukungan runtime
    • Ini bisa memerlukan banyak pembacaan user space terhadap bagian dalam runtime, sehingga overhead-nya tinggi
    • Karena rentan terhadap perubahan runtime, pendekatan ini lebih cocok didistribusikan dan dipelihara bersama code base bahasa tersebut
  • ORC(oops rewind capability): stack unwinder ringan milik kernel Linux, yang memungkinkan kernel baru menghapus frame pointer sambil tetap mempertahankan stack walking
  • SFrames(Stack Frames): metode unwinding stack pengguna yang ringan dan berbasis ORC
  • Shadow Stacks: fitur keamanan Intel dan AMD yang mendorong return address fungsi ke stack hardware terpisah untuk diverifikasi saat return, dan tampaknya juga dapat dimanfaatkan untuk stack trace

Apa yang berubah sekarang juga

  • Alasan penghilangan frame pointer pada 2004—keuntungan performa i386, penilaian kompatibilitas debugger saat itu, dan persaingan dengan icc—sudah tidak cocok dengan situasi 2024
  • Penghapusan frame pointer yang diterapkan ke x86-64 bahkan saat itu pun dinilai tidak memberi manfaat yang cukup dibanding hilangnya kemampuan debugging
  • Dengan Fedora dan Ubuntu mengembalikan frame pointer, orang yang memakai rilis 2024 akan lebih mudah memahami CPU Flame Graph dan untuk pertama kalinya dapat benar-benar memanfaatkan Off-CPU Flame Graph
  • Continuous profiler juga menjadi lebih mudah mendapatkan profil yang lebih lengkap tanpa harus meminta pelanggan mengubah OS mereka
  • Di masa depan mungkin stack trace bisa kembali dilakukan tanpa frame pointer lewat pendekatan seperti SFrames atau Shadow Stacks, tetapi perbaikan paling nyata yang dapat dilakukan sekarang adalah menyalakan frame pointer sebagai default

1 komentar

 
GN⁺ 2024-03-18
Opini Hacker News
  • Saya ingat masa awal 2000-an ketika penghilangan stack frame pointer mulai menyebar
    Saat itu saya belajar ilmu komputer di sebuah universitas di negara dunia ketiga yang miskin, dan karena komputer yang tua dan lambat, sebagian besar tugas memakai compiler ketimbang interpreter
    Ada banyak mata kuliah menarik, mulai dari mengimplementasikan struktur data level rendah, compiler, rutinitas numerik assembly, sampai driver perangkat Minix, dan ketika program berperilaku aneh, saya menempelkan gdb lalu men-debug langsung di level assembly sambil menelusuri stack
    Namun tiba-tiba -fomit-frame-pointer menjadi tren, dan stack trace mulai menjadi tidak masuk akal; debugging segfault atau illegal instruction jadi jauh lebih sulit
    Pada akhirnya, untuk menghindari sesi debugging yang rusak, saya jadi memakai Python untuk hampir semua hal; performa hilang satu-dua digit, tetapi belajar Python kemudian berguna

    • Saya penasaran apakah ia tidak tahu tentang -fno-omit-frame-pointer
  • Senang melihat Fedora disebut. Mempertahankan frame pointer tetap aktif di seluruh distro adalah perjuangan yang cukup melelahkan
    Contoh: https://pagure.io/fesco/issue/3084
    Mitos bahwa overhead frame pointer besar masih terus ada, dan itu berasal dari satu kasus Python yang melambat +10%, yang sekarang sudah diperbaiki
    Overhead yang benar-benar terukur kurang dari 1%, sementara manfaat yang diperoleh pada aplikasi tertentu jauh lebih besar

    • Pernyataan bahwa “overhead terukur yang sebenarnya kurang dari 1%” tampak berlebihan. Overhead sangat bergantung pada workload yang diukur, sehingga sulit diterapkan secara universal
      Itu juga tidak terlalu sesuai dengan pengukuran di sisi kernel Linux; di sana saya melihat kisarannya 5–10%: https://lore.kernel.org/lkml/20170602104048.jkkzssljsompjdwy...
      Pada berbagai workload seperti netperf, mikrobenchmark page allocator, pgbench, dan sqlite, mengaktifkan frame pointer menimbulkan overhead 5–10%, dan penting bahwa dampaknya terlihat pada PostgreSQL dan SQLite
      DBMS adalah salah satu cara yang baik untuk memberi tekanan kuat pada sistem
    • Agar flame graph berfungsi dengan benar di OCaml 5, kita harus memakai frame pointer, atau memakai LBR yang kedalamannya terbatas dan mungkin tidak bekerja tergantung CPU, atau melakukan perubahan besar pada sisi perf/eBPF agar menangani dua stack milik OCaml
      OCaml 5 memakai stack terpisah untuk kode OCaml dan kode C, dan GDB dapat menghubungkan keduanya dengan informasi DWARF, tetapi call graph DWARF milik perf tidak bisa: https://github.com/ocaml/ocaml/issues/12563#issuecomment-193...
      Jika perlu alasan untuk mempertahankan frame pointer di rilis mendatang, OCaml 5 bisa dijadikan contoh
      Saya baru tahu bahwa Fedora 39 sudah mengaktifkan frame pointer secara default, sementara profiling sehari-hari saya masih kebanyakan dilakukan di sistem mirip CentOS 7 yang memakai perf record --call-graph dwarf -F 47 -a
    • Di 32-bit, frame pointer masih menyulitkan, jadi tidak cocok untuk bidang seperti IoT saat ini
      Alasan menghapus frame pointer bukanlah mitos, melainkan berasal dari realitas zaman sebelum 64-bit, dan itu juga belum terlalu lama
      Bahkan sekarang, untuk memberi napas baru pada sistem 64-bit lama, optimasi semacam ini masih bermakna
      Idealnya, ini juga harus menjadi default pada sistem yang keamanan-nya penting, dan tidak semuanya perlu dioptimalkan demi “observabilitas”
    • Saya rasa overhead “kurang dari 1%” semacam itu menumpuk dan membuat pengalaman memakai komputer secara terukur lebih lambat dibanding 30 tahun lalu. Overhead kecil seperti itu pada akhirnya terakumulasi
  • Ada satu hal yang Apple lakukan dengan baik di ARM: membuat x29 frame pointer selalu menunjuk ke frame record yang valid
    Beberapa fungsi seperti leaf function atau tail call mungkin tidak membuat entri dalam daftar, tetapi hasilnya stack trace selalu tetap bermakna meski tanpa informasi debug
    https://developer.apple.com/documentation/xcode/writing-arm6...

    • Di platform Apple, sering ada jenis masalah keterinterpretasian lain. Aplikasi Objective-C / Swift memiliki banyak blok dan closure yang tersarang dalam, sehingga backtrace kerap tersebar di beberapa thread, dan saya belum tahu solusi yang bagus
  • Pada 2005 saya berada di kubu seberang di Google, dan pemikiran saat itu sederhana
    Sekalipun $BIG_COMPANY memutuskan mengompilasi semuanya dengan frame pointer, komunitas lainnya tidak akan melakukan itu, sehingga kami akan terus berada dalam perdebatan yang tak mungkin dimenangkan melawan komunitas yang jauh lebih besar
    Pada akhirnya perdebatan itu menjadi hampir perdebatan 20 tahun, dan kemudian saya menulis patch agar libunwind bekerja di gperftools lalu selama beberapa tahun memelihara libunwind
    Sekarang saya sudah pindah ke bidang komputasi lain dan menjadi pengamat pasif, tetapi menarik membaca sejarah dari sudut pandang kubu seberang

    • Saya penasaran, dengan cara apa menambahkan frame pointer bisa menjadi penghambat. Saya tidak tahu masalah fungsional apa yang akan muncul
  • Jika RBP akan diserahkan sebagai frame pointer, sebenarnya mungkin juga memakai dua stack
    Satu ditunjuk oleh RBP dan menyimpan frame aktivasi, sementara yang lain ditunjuk oleh RSP dan hanya menyimpan alamat return
    Dengan begitu, call stack secara harfiah menjadi array datar berisi alamat return, sehingga tidak perlu lagi “menelusuri stack”
    Saya juga tidak tahu kenapa alamat return harus disimpan dekat variabel lokal sejak awal, dan kelemahannya tampak terlalu banyak

    • Karena manajemen penyimpanannya jadi sederhana. Stack frame adalah bump pointer sederhana yang selalu ada di cache, dan untuk overflow hanya perlu satu guard page
      Cara yang diusulkan perlu dua guard page, manipulasi stack juga menjadi dua kali lipat, dan kemungkinan cache miss pun menjadi dua kali lipat
    • Fitur CPU yang disinggung sekilas dalam tulisan, yaitu shadow stack, cukup mirip dengan ini. Namun tujuan utamanya adalah keamanan
    • Saya juga bertanya-tanya mengapa stack tumbuh ke “arah yang salah” sehingga program yang berperilaku salah bisa menimbulkan masalah keamanan
      Saya tahu alasannya, tetapi seperti banyak hal lain, itu terakhir masuk akal sekitar 30 tahun lalu, dan dampaknya menarik
    • Sepertinya Anda sudah siap untuk menyukai Forth ;-)
      Anehnya, artikel Wikipedia tampaknya tidak terlalu menonjolkan bahwa Forth bisa mengakses baik stack parameter maupun stack return sebagai ciri utama modelnya
      https://en.wikipedia.org/wiki/Forth_(programming_language)
    • Manfaat menyimpan alamat return di tempat lain tidak jelas kecuali hardware mendukung sesuatu seperti shadow stack
      Perlu ditunjukkan bahwa biaya memindahkannya ke page terpisah dan mengelola dua pointer secara nyata lebih murah daripada stack cookie/protector, yang sudah memberikan proteksi hanya di tempat yang dibutuhkan
      Itu juga tidak punya manfaat keamanan yang nyata dibanding stack protector saat ini. Jika baca/tulis arbitrer dimungkinkan, pada akhirnya itu bisa mengarah pada bypass integritas alur kontrol
  • Virgil tidak memakai frame pointer. Jika tidak ada alokasi stack dinamis, ukuran frame untuk fungsi tertentu bersifat tetap dan bisa ditemukan dengan lookup tabel pencarian biner sederhana
    Teknik Virgil juga memakai rentang berbasis indeks page untuk mempersempit pencarian hanya dengan beberapa perbandingan secara rata-rata, menggabungkan informasi unwinding dengan stackmap untuk GC, dan memakai ruang yang sangat kecil
    Kode utamanya ada di https://github.com/titzer/virgil/blob/master/rt/native/Nativ..., dan sisa kode di direktori yang sama mengimplementasikan decoding metadata
    Menurut saya frame pointer hanya masuk akal ketika ukuran frame dinamis, yaitu ketika mengalokasikan data di stack
    Aneh memakai mekanisme dinamis padahal mekanisme statis sudah cukup; tampaknya ini terutama karena tidak ada kesepakatan soal ABI encoding metadata atau rutin unwinding
    Angka pengukuran 1–2% bisa dipercaya, dan kira-kira setara dengan biaya pemeriksaan batas array
    Memberi perlakuan khusus pada biaya 1% untuk debugging dan profiling, tetapi merasa enggan menambahkan lapisan keamanan, menunjukkan prioritas yang sangat aneh

    • Pemeriksaan batas juga bisa ditambahkan ke C, tetapi biayanya jauh lebih besar daripada 1–2%
      std::vector di C++ mematikan pemeriksaan batas secara default, yang menurut saya karena C++ dirancang oleh orang-orang yang benar-benar gila, dan untuk orang-orang seperti itu juga
      Selain itu, tidak ada bahasa tanpa pemeriksaan batas yang langsung terpikir oleh saya
  • Tulisan yang bagus. Saya merasa sayang ketika frame pointer menghilang
    Di Linux, seperti juga di sistem lain, banyak orang lama menderita karena ketiadaan frame pointer, dan berusaha mempertahankannya di sebanyak mungkin lingkungan
    Melihat Linux arus utama membawanya kembali terasa seperti mendapat pengakuan, tetapi sekaligus agak membuat frustrasi

    • Saya sungguh penasaran. Saya tahu unwinding stack dengan DWARF itu merepotkan, tetapi saya tidak mengerti mengapa itu begitu buruk sampai harus memperlambat kode seluruh sistem
      Di keluarga Debian, itu lambat karena alasan lisensi membuat perf hanya memaketkan jalur unwinding yang lambat, tetapi kalau alatnya bagus, perbedaannya hampir tidak terasa
      Saya penasaran apa yang saya lewatkan
  • Secara umum saya mendukung frame pointer, tetapi setelah bekerja beberapa tahun di area ini, ada beberapa hal yang saya rasakan.
    Banyak stack unwinding berbasis frame pointer tidak mempertimbangkan masalah yang tidak ada pada informasi unwind DWARF. Penyiapan frame tidak atomik dan terdiri dari dua instruksi, push $rbp dan mov $rsp $rbp; jika snapshot diambil saat push, frame induk bisa terlewat.
    Mungkin ini bisa dimitigasi dengan memeriksa kode, tetapi bisa saja ada push %rbp yang tidak terkait dengan stack frame, jadi ini terasa lebih seperti heuristik.
    Saya juga mengembangkan solusi unwinding cepat di dalam kernel berbasis BPF yang disebut Brendan: https://web.archive.org/web/20231222054207/https://www.polar...
    Pendekatan ini tidak memakai DWARF CFI apa adanya, melainkan mengonversinya ke format akses acak yang bisa digunakan di BPF.
    Saat ini hanya mendukung section JIT yang memiliki frame pointer, tetapi saya rasa memungkinkan untuk mengimplementasikan unwinding interpreter JVM agar terjalin dengan unwinding native.
    Idealnya, pengaktifan frame pointer dilakukan per kasus, dan benchmarking adalah kuncinya.
    Bergantung pada industri dan sifat perangkat lunak, trade-off antara performa, observability, dan metrik bisnis bisa sangat berbeda.
    Pihak Fedora bekerja dengan sangat baik dan ketat dalam hal ini.
    Selain itu, build system yang dapat mengubah pengaturan ini di seluruh sistem, termasuk library dependensi, sangat berguna bukan hanya untuk pengujian tetapi juga untuk penerapan di produksi.
    Terakhir, saya menantikan SFrame yang sedang dikerjakan Indu. Sepertinya ini akan menyelesaikan banyak masalah saat ini sambil memungkinkan pengguna memilih apakah akan memakai frame pointer atau tidak, tetapi mungkin perlu beberapa tahun sampai infrastrukturnya siap dan semua orang melakukan upgrade.

    • Untuk mendapatkan flame graph yang bagus, frame pointer harus diaktifkan di seluruh distribusi Linux.
      Untuk memahami apa yang sebenarnya terjadi, diperlukan analisis seluruh sistem, dan dalam struktur distribusi Linux biner saat ini seperti Fedora atau Debian, praktis tidak ada alternatif lain.
    • Bisa juga dilakukan dengan satu instruksi: ENTER N,0 mencadangkan ruang stack sebesar N untuk variabel lokal dan kurang lebih sama dengan PUSH EBP, MOV ESP,ESP, SUB SP,N.
      Namun saya tidak ingat apakah ENTER ada di x86-64.
      Meski begitu, antara CALL dan penyiapan frame tetap tidak atomik, jadi jika snapshot diambil setelah CALL tetapi sebelum ENTER, penyiapan frame tidak akan diperoleh.
      Alasan ENTER jarang dipakai adalah karena dianggap terlalu lambat.
      LEAVE dipakai karena sama cepat atau lebih cepat daripada rangkaian instruksi penggantinya, tetapi pada ENTER, operand kedua merusak performa.
      Operand itu ditujukan agar fungsi bersarang dapat mengakses stack frame di atasnya, dan biaya penggunaannya sangat besar.
    • Penyiapan frame yang tidak atomik jelas menjadi masalah bagi CPU profiler, tetapi tidak menjadi masalah bagi allocation profiling, profiling Off-CPU, dan jenis profiling lain yang tidak berbasis interrupt.
    • Di sisi unwinding JVM juga ada kemajuan yang baik.
  • Menarik bahwa sekarang saya akhirnya paham mengapa gunung [unknown] yang terlihat di profil itu muncul.
    Namun ini tidak mudah dibenarkan. Perbedaan performa 2% sebenarnya cukup besar.
    Akan bagus jika penyertaan frame pointer bisa dikendalikan dengan lebih granular.
    Dengan profiling yang granular, seharusnya bisa ditentukan apakah fungsi atau unit kompilasi tertentu membutuhkan frame pointer.
    Saya tidak akan terkejut jika hasilnya menunjukkan bahwa hanya sedikit pekerjaan yang melambat drastis karena penyertaan frame pointer, sementara sisanya hampir tidak terpengaruh.

    • Perbedaan performa 2% tidak sebesar itu. Apalagi jika lewat profiling kita bisa menemukan bottleneck dan memperoleh peningkatan lebih dari 10%.
    • Di GCC, ini bisa dinyalakan dan dimatikan per fungsi dengan menambahkan atribut pada deklarasi fungsi. Namun di LLVM ini tidak berfungsi.
      __attribute__((optimize("no-omit-frame-pointer")))
      __attribute__((optimize("omit-frame-pointer")))
    • Pada aplikasi nyata, biaya performanya bisa jauh lebih kecil dari 2%.
      Benchmark seperti ini agak artifisial, jadi tidak boleh dipercaya sepenuhnya, dan hasil aplikasi dunia nyata sering kali sangat berbeda.
      Profiling itu penting; saya pernah melakukan profiling kode dengan cermat dan membuat beberapa bagian hingga 20% lebih cepat.
      Jika aplikasinya sangat sensitif terhadap penurunan performa, di lab Anda bisa mengaktifkan frame pointer dan melakukan profiling, lalu menghilangkannya pada versi yang didistribusikan ke pelanggan.
    • Overhead yang terukur sedikit di bawah 1%. Dulu ada kasus langka di mana performa memburuk signifikan karena frame pointer, tetapi kini sudah diperbaiki.
    • Biasanya jauh lebih kecil dari 2%.
  • Dukungan untuk kode JIT sayangnya tidak bagus, tetapi LLVM memiliki hook yang sangat baik untuk mencatat setiap method yang dihasilkan beserta alamatnya.
    Jadi mixed-mode stack unwinding sederhana bisa dibuat relatif mudah, meski terutama dimungkinkan dari dalam proses.
    Rangkaian DNN milik Intel tampaknya men-dump informasi ke file publik yang bisa dibaca perf, tetapi kernel oneDNN sendiri terus menggunakan ulang RBP, sehingga praktis tidak berguna.
    Klaim dalam tulisan bahwa “runtime JIT seperti Java JVM tidak memiliki informasi DWARF” juga mengejutkan.
    Saya penasaran apakah itu dimatikan secara default, atau memang secara harfiah tidak tersedia.
    Jika dicari, biasanya ujung-ujungnya membahas keinginan untuk memasukkan sisi JNI/C ke dalam stack JVM: https://github.com/async-profiler/async-profiler/issues/215