Mengapa perbandingan begitu mahal pada GPU?

10

Ketika mencoba untuk meningkatkan kinerja kelas deteksi tabrakan saya, saya menemukan bahwa ~ 80% dari waktu yang dihabiskan di GPU, dihabiskan untuk jika kondisi lain hanya mencoba mencari batas untuk ember yang harus dilingkari.

Lebih tepatnya:

  1. setiap utas mendapat ID, dengan ID itu ia mengambil segitiga dari memori (masing-masing 3 bilangan bulat) dan oleh mereka 3 ia mengambil simpulnya (masing-masing 3 mengapung).

  2. Kemudian mengubah simpul menjadi titik-titik kotak integer (saat ini 8x8x8) dan mengubahnya menjadi batas segitiga pada kotak itu

  3. Untuk mengubah 3 titik menjadi batas, ia menemukan min / maks dari setiap dimensi di antara masing-masing titik

Karena bahasa pemrograman yang saya gunakan tidak memiliki intrinsik minmax, saya membuatnya sendiri, terlihat seperti ini:

procedure MinMax(a, b, c):
   local min, max

   if a > b:
      max = a
      min = b
   else:
      max = b
      min = a
   if c > max:
      max = c
   else:
      if c < min:
         min = c

   return (min, max)

Jadi rata-rata seharusnya perbandingan 2,5 * 3 * 3 = 22,5 yang berakhir memakan waktu jauh lebih banyak daripada tes persimpangan segitiga-tepi yang sebenarnya (sekitar 100 * 11-50 instruksi).

Bahkan, saya menemukan bahwa pre-menghitung ember yang diperlukan pada cpu (berulir tunggal, tidak ada vektorisasi), menumpuknya dalam tampilan gpu bersama dengan definisi bucket dan membuat gpu melakukan ~ 4 pembacaan tambahan per thread adalah 6 kali lebih cepat daripada mencoba untuk mencari tahu batas di tempat. (perhatikan bahwa mereka akan dihitung ulang sebelum setiap eksekusi karena saya berurusan dengan jaring dinamis)

Jadi mengapa perbandingannya begitu lambat pada GPU?

pengguna29075
sumber
2
Pertanyaan Anda adalah tentang kinerja tingkat instruksi dari bagian kode tertentu pada jenis perangkat keras tertentu. Bagi saya itu kedengarannya lebih mirip pertanyaan pemrograman daripada pertanyaan sains komputer.
David Richerby
7
Dugaan saya adalah bahwa bukan perbandingan yang mahal tetapi cabang-cabangnya. Jika kompiler tidak menggunakan predikasi (atau GPU tidak menyediakannya), cabang akan digunakan yang menyebabkan forking "utas" (karena GPU berorientasi pada SIMD). Mengubah kondisi menjadi topeng dan menggunakan topeng untuk mensintesis gerakan bersyarat / bertukar mungkin merupakan alternatif yang masuk akal.
Paul A. Clayton
1
@ DavidRicherby Saya tidak yakin apakah itu spesifik. Bukankah pertanyaan ini berlaku untuk arsitektur SIMD?
kasperd
1
@DavidRicherby: alasan kami mengajarkan lengkungan comp di departemen CS adalah karena lengkungan comp berdampak pada algoritma yang Anda pilih. Arsitektur SIMD dapat menghasilkan throughput tinggi hanya jika Anda dapat mengetahui cara menulis program tanpa cabang bersarang.
Pengembaraan Logika
2
Sebagai jawaban oleh Wandering Logic menyatakan dengan cara yang kurang jelas, GPU bekerja dengan mengasumsikan bahwa banyak "utas" berada pada instruksi yang sama secara bersamaan. Jadi GPU, secara kasar, mengambil setiap cabang daripada hanya cabang yang benar. Inilah sebabnya mengapa GPU mengeksploitasi fakta bahwa tetangga biasanya mengambil cabang yang sama; dan kinerja sangat buruk ketika ini tidak benar.
Rob

Jawaban:

10

GPU adalah arsitektur SIMD. Dalam arsitektur SIMD, setiap instruksi perlu dijalankan untuk setiap elemen yang Anda proses. (Ada pengecualian untuk aturan ini, tetapi jarang membantu).

Jadi dalam MinMaxrutinitas Anda, tidak hanya setiap panggilan perlu mengambil ketiga instruksi cabang, (bahkan jika rata-rata hanya 2,5 dievaluasi), tetapi setiap pernyataan penugasan mengambil siklus juga (bahkan jika itu tidak benar-benar "dieksekusi" ).

Masalah ini kadang-kadang disebut divergence thread . Jika mesin Anda memiliki sesuatu seperti 32 jalur eksekusi SIMD, ia hanya akan memiliki satu unit pengambilan tunggal. (Di sini istilah "utas" pada dasarnya berarti "jalur eksekusi SIMD".) Jadi secara internal setiap jalur eksekusi SIMD memiliki bit "Saya diaktifkan / dinonaktifkan", dan cabang-cabang sebenarnya hanya memanipulasi bit itu. (Pengecualian adalah pada titik di mana setiap jalur SIMD dinonaktifkan, unit pengambilan umumnya akan langsung melompat ke klausul "lain".)

Jadi dalam kode Anda, setiap jalur eksekusi SIMD melakukan:

compare (a > b)
assign (max = a if a>b)
assign (min = b if a>b)
assign (max = b if not(a>b))
assign (min = a if not(a>b))
compare (c > max)
assign (max = c if c>max)
compare (c < min if not(c>max))
assign (min = c if not(c>max) and c<min)

Mungkin pada beberapa GPU konversi persyaratan untuk predikasi ini lebih lambat jika GPU melakukannya sendiri. Seperti yang ditunjukkan oleh @ PaulA.Clayton, jika bahasa pemrograman dan arsitektur Anda memiliki operasi pemindahan bersyarat yang telah ditentukan (terutama salah satu formulir if (c) x = y else x = z), Anda mungkin dapat melakukan yang lebih baik. (Tapi mungkin tidak jauh lebih baik).

Juga, menempatkan c < mindalam bersyarat elsedari c > maxtidak diperlukan. Ini tentu saja tidak menyelamatkan Anda apa pun, dan (mengingat bahwa GPU harus secara otomatis mengubahnya menjadi predikasi) sebenarnya mungkin menyakitkan untuk membuatnya bersarang dalam dua kondisi yang berbeda.

Logika Pengembaraan
sumber
2
(Maaf jika ada bagian dari ini yang tidak jelas, saya mencoba untuk mendapatkan jawaban sebelum para ahli teori menutup pertanyaan sebagai off topic.)
Wandering Logic
Untuk lebih lanjut tentang dasar-dasarnya: http.developer.nvidia.com/GPUGems2/gpugems2_chapter34.html Dan untuk solusi yang lebih baru: eecis.udel.edu/~cavazos/cisc879/papers/a3-han.pdf
Fizz
Ini adalah topik dalam arti bahwa beberapa algoritma tidak dapat dipercepat melalui paralelisme SIMD. (yaitu: Bekerja, Rentang, dll untuk perawatan yang lebih teoretis tentang mengapa)
Rob
1
Berikut adalah kuliah lain tentang dasar-dasar divergence people.maths.ox.ac.uk/gilesm/cuda/lecs/lec3-2x2.pdf Perhatikan dari sini bahwa masalahnya (pada Nvidia tetap) hanya per-warp. Kode yang berjalan pada warps yang berbeda dapat dengan senang hati menyimpang. Dan makalah lain mengusulkan metode untuk menghindarinya: hal.inria.fr/file/index/docid/649650/filename/sbiswi.pdf
Fizz
Pada cara yang sedikit berbeda, tetapi sejalan dengan komentar yang saya tulis di bawah pertanyaan eprint.iacr.org/2012/137.pdf layak dibaca: perlambatan 10x dibandingkan dengan prediksi kinerja dapat menjadi "normal" untuk GPU kecuali Anda turun ke perakitannya (biasanya dengan alat yang secara resmi tidak didukung). Mungkin saja kompiler penargetan GPU menjadi lebih baik, tetapi saya tidak akan menahan nafas.
Fizz