Pemrogram seharusnya memiliki ide yang cukup baik tentang biaya operasi tertentu: misalnya biaya instruksi pada CPU, biaya L1, L2, atau L3 cache miss, biaya LHS.
Ketika datang ke grafis, saya menyadari saya punya sedikit atau tidak tahu apa itu. Saya ada dalam pikiran bahwa jika kita memesannya berdasarkan biaya, perubahan status adalah seperti:
- Perubahan seragam shader.
- Perubahan buffer vertex aktif.
- Unit perubahan tekstur aktif.
- Perubahan program shader aktif.
- Perubahan buffer bingkai aktif.
Tapi itu adalah aturan yang sangat kasar, bahkan mungkin tidak benar, dan saya tidak tahu apa urutan besarnya. Jika kita mencoba meletakkan unit, ns, siklus jam atau jumlah instruksi, berapa banyak yang kita bicarakan?
sumber
Biaya aktual dari perubahan negara tertentu bervariasi dengan begitu banyak faktor sehingga jawaban umum hampir tidak mungkin.
Pertama, setiap perubahan kondisi berpotensi memiliki biaya sisi CPU dan biaya sisi GPU. Biaya CPU mungkin, tergantung pada driver Anda dan API grafik, dibayar seluruhnya pada utas utama atau sebagian pada utas latar belakang.
Kedua, biaya GPU mungkin tergantung pada jumlah pekerjaan dalam penerbangan. GPU modern sangat pipelined dan suka mendapatkan banyak pekerjaan dalam penerbangan sekaligus, dan perlambatan terbesar yang bisa Anda dapatkan adalah dengan menghentikan pipa sehingga semua yang saat ini dalam penerbangan harus pensiun sebelum keadaan berubah. Apa yang bisa menyebabkan saluran pipa? Yah, itu tergantung GPU Anda!
Hal yang perlu Anda ketahui untuk memahami kinerja di sini adalah: apa yang perlu dilakukan oleh driver dan GPU untuk memproses perubahan status Anda? Ini tentu saja tergantung pada GPU Anda, dan juga pada detail yang sering tidak dibagikan ISV kepada publik. Namun, ada beberapa prinsip umum .
GPU umumnya dibagi menjadi frontend dan backend. Frontend menangani aliran perintah yang dihasilkan oleh driver, sementara backend melakukan semua pekerjaan nyata. Seperti yang saya katakan sebelumnya, backend suka memiliki banyak pekerjaan dalam penerbangan, tetapi perlu beberapa informasi untuk menyimpan informasi tentang pekerjaan itu (mungkin diisi oleh frontend). Jika Anda menendang batch yang cukup kecil dan menggunakan semua silikon melacak pekerjaan, maka frontend harus berhenti bahkan jika ada banyak tenaga kuda yang tidak terpakai duduk di sekitar. Jadi prinsipnya di sini: semakin banyak perubahan status (dan undian kecil), semakin besar kemungkinan Anda membuat kelaparan GPU backend .
Sementara undian sebenarnya sedang diproses, Anda pada dasarnya hanya menjalankan program shader, yang melakukan akses memori untuk mengambil seragam Anda, data buffer vertex Anda, tekstur Anda, tetapi juga struktur kontrol yang memberi tahu unit shader di mana vertex Anda buffer dan tekstur kamu. Dan GPU memiliki cache di depan akses memori itu juga. Jadi, setiap kali Anda melempar seragam baru atau binding tekstur / buffer baru pada GPU, kemungkinan akan mengalami kehilangan cache saat pertama kali harus membacanya. Prinsip lain: sebagian besar perubahan status akan menyebabkan cache GPU kehilangan.(Ini paling berarti ketika Anda mengelola buffer konstan sendiri: jika Anda menjaga buffer konstan sama di antara pengundian, maka mereka cenderung untuk tetap berada dalam cache pada GPU.)
Sebagian besar biaya untuk perubahan negara untuk sumber daya shader adalah sisi CPU. Setiap kali Anda mengatur buffer konstan baru, driver kemungkinan besar menyalin konten buffer konstan itu ke dalam aliran perintah untuk GPU. Jika Anda menetapkan satu seragam, driver sangat mungkin mengubahnya menjadi buffer konstan besar di belakang Anda, sehingga harus mencari offset untuk seragam itu di buffer konstan, salin nilainya, kemudian tandai buffer konstan sebagai kotor sehingga dapat disalin ke dalam aliran perintah sebelum pengundian berikutnya. Jika Anda mengikat buffer vertex tekstur baru, driver mungkin menyalin struktur kontrol untuk sumber daya sekitar. Juga, jika Anda menggunakan GPU diskrit pada OS multitasking, driver harus melacak setiap sumber daya yang Anda gunakan dan ketika Anda mulai menggunakannya sehingga kernel ' Manajer memori GPU dapat menjamin bahwa memori untuk sumber daya tersebut ada di VRAM GPU saat pengundian terjadi. Prinsip:perubahan status membuat driver mengocok memori sekitar untuk menghasilkan aliran perintah minimal untuk GPU.
Ketika Anda mengubah shader saat ini, Anda mungkin menyebabkan kehilangan cache GPU (mereka juga memiliki cache instruksi!). Pada prinsipnya, pekerjaan CPU harus dibatasi untuk menempatkan perintah baru di aliran perintah yang mengatakan "gunakan shader." Namun pada kenyataannya, ada banyak kompilasi shader yang harus ditangani. Driver GPU sangat sering mengkompilasi shader dengan malas, bahkan jika Anda telah membuat shader sebelumnya. Lebih relevan dengan topik ini, beberapa negara bagian tidak didukung secara asli oleh perangkat keras GPU dan malah dikompilasi ke dalam program shader. Salah satu contoh populer adalah format vertex: ini dapat dikompilasi ke dalam vertex shader alih-alih keadaan terpisah pada chip. Jadi jika Anda menggunakan format vertex yang belum pernah Anda gunakan dengan vertex shader tertentu sebelumnya, Anda sekarang dapat membayar banyak biaya CPU untuk menambal shader dan menyalin program shader ke GPU. Selain itu, kompiler driver dan shader dapat berkonspirasi untuk melakukan segala hal untuk mengoptimalkan eksekusi program shader. Ini mungkin berarti mengoptimalkan tata letak memori seragam Anda dan struktur kontrol sumber daya sehingga mereka dikemas dengan baik ke memori yang berdekatan atau register shader. Jadi ketika Anda mengubah shader, ini dapat menyebabkan driver melihat semua yang telah Anda ikat ke pipeline dan mengemasnya kembali ke format yang sama sekali berbeda untuk shader baru, dan kemudian menyalinnya ke dalam aliran perintah. Prinsip: Ini mungkin berarti mengoptimalkan tata letak memori seragam Anda dan struktur kontrol sumber daya sehingga mereka dikemas dengan baik ke memori yang berdekatan atau register shader. Jadi ketika Anda mengubah shader, ini dapat menyebabkan driver melihat semua yang telah Anda ikat ke pipeline dan mengemasnya kembali ke format yang sama sekali berbeda untuk shader baru, dan kemudian menyalinnya ke dalam aliran perintah. Prinsip: Ini mungkin berarti mengoptimalkan tata letak memori seragam Anda dan struktur kontrol sumber daya sehingga mereka dikemas dengan baik ke memori yang berdekatan atau register shader. Jadi ketika Anda mengubah shader, ini dapat menyebabkan driver melihat semua yang telah Anda ikat ke pipeline dan mengemasnya kembali ke format yang sama sekali berbeda untuk shader baru, dan kemudian menyalinnya ke dalam aliran perintah. Prinsip:mengubah shader dapat menyebabkan banyak pengocokan memori CPU.
Perubahan buffer bingkai mungkin yang paling tergantung pada implementasi, tetapi umumnya cukup mahal pada GPU. GPU Anda mungkin tidak dapat menangani beberapa panggilan undian ke target render yang berbeda secara bersamaan, sehingga mungkin perlu menghentikan jalur pipa antara dua panggilan draw tersebut. Mungkin perlu membilas cache agar target render dapat dibaca nanti. Mungkin perlu menyelesaikan pekerjaan yang telah ditunda selama gambar. (Sangat umum untuk mengakumulasi struktur data terpisah bersama dengan buffer dalam, target render MSAA, dan banyak lagi. Ini mungkin perlu diselesaikan ketika Anda beralih dari target render itu. Jika Anda menggunakan GPU yang berbasis ubin , seperti banyak GPU seluler, sejumlah besar pekerjaan bayangan yang sebenarnya mungkin perlu dibilas ketika Anda beralih dari frame buffer.) Prinsip:mengubah target render mahal pada GPU.
Saya yakin itu semua sangat membingungkan, dan sayangnya sulit untuk menjadi terlalu spesifik karena detailnya sering kali tidak terbuka untuk umum, tetapi saya berharap ini adalah gambaran setengah layak dari beberapa hal yang sebenarnya terjadi ketika Anda memanggil beberapa negara bagian. mengubah fungsi di API grafik favorit Anda.
sumber