หลีกเลี่ยงถ้างบใน DirectX 10 shaders หรือไม่


14

ฉันเคยได้ยินว่าถ้างบควรหลีกเลี่ยงใน shaders เพราะทั้งสองส่วนของงบจะดำเนินการและกว่าจะผิดพลาดจะลดลง (ซึ่งเป็นอันตรายต่อประสิทธิภาพการทำงาน)

ยังคงเป็นปัญหาใน DirectX 10 หรือไม่ มีคนบอกฉันว่ามีเพียงสาขาที่ถูกต้องเท่านั้นที่จะถูกประหาร

สำหรับภาพประกอบฉันมีรหัส:

float y1 = 5; float y2 = 6; float b1 = 2; float b2 = 3;

if(x>0.5){
    x = 10 * y1 + b1;
}else{
    x = 10 * y2 + b2;
}

มีวิธีอื่นที่จะทำให้เร็วขึ้นหรือไม่?

ถ้าเป็นเช่นนั้นได้อย่างไร

กิ่งทั้งสองมีลักษณะคล้ายกันความแตกต่างเพียงอย่างเดียวคือค่าของ "ค่าคงที่" ( y1, y2, b1, b2เหมือนกันสำหรับพิกเซลทั้งหมดใน Pixel Shader)


1
สุจริตนั่นคือการเพิ่มประสิทธิภาพก่อนวัยอันควรเพียงแค่ไม่เปลี่ยนจนกว่าคุณจะทำการทดสอบโค้ดของคุณและเป็น 100% ที่ shader เป็นคอขวด
pwny

คำตอบ:


17

กฎหลายข้อในการเพิ่มประสิทธิภาพการปรับขนาดเล็กเหมือนกับซีพียูแบบดั้งเดิมที่มีส่วนขยายแบบเวกเตอร์ นี่เป็นคำใบ้:

  • มีฟังก์ชั่นการทดสอบในตัว ( test, lerp/ mix)
  • การบวกเวกเตอร์สองตัวมีค่าใช้จ่ายเช่นเดียวกับการบวกสองลอย
  • ฟรี

มันเป็นความจริงที่ว่าสาขามีราคาถูกกว่าฮาร์ดแวร์ที่ทันสมัยกว่าที่เคยเป็น แต่ก็ยังดีกว่าที่จะหลีกเลี่ยงถ้าเป็นไปได้ ด้วยการใช้ฟังก์ชั่น swizzling และ test คุณสามารถเขียน shader ของคุณใหม่ได้โดยไม่ต้องทำการทดสอบ:

/* y1, y2, b1, b2 */
float4 constants = float4(5, 6, 2, 3);

float2 tmp = 10 * constants.xy + constants.zw;
x = lerp(tmp[1], tmp[0], step(x, 0.5));

การใช้stepและlerpเป็นสำนวนที่ใช้กันทั่วไปมากสำหรับการเลือกระหว่างสองค่า


6

โดยทั่วไปแล้วมันก็โอเค Shaders จะดำเนินการในกลุ่มจุดยอดหรือพิกเซล (ผู้ขายต่างมีคำศัพท์ที่แตกต่างกันสำหรับสิ่งเหล่านี้ดังนั้นฉันจะเก็บห่างจากที่) และถ้าจุดยอดหรือพิกเซลทั้งหมดในกลุ่มใช้เส้นทางเดียวกันแล้วค่าใช้จ่ายในการแยกสาขา

คุณต้องเชื่อถือคอมไพเลอร์ shader รหัส HLSL ที่คุณเขียนไม่ควรถูกมองว่าเป็นตัวแทนโดยตรงของ bytecode หรือแม้กระทั่งชุดที่มันจะรวบรวมลงไปและคอมไพเลอร์มีอิสระอย่างสมบูรณ์ในการแปลงเป็นสิ่งที่เทียบเท่า แต่หลีกเลี่ยงสาขา (เช่น lerp อาจเป็นบางครั้ง การแปลงที่ต้องการ) ในทางกลับกันหากคอมไพเลอร์พิจารณาว่าการดำเนินการสาขาเป็นเส้นทางที่เร็วกว่าจริง ๆ แล้วจะรวบรวมไปยังสาขา การดูแอสเซมบลีที่สร้างขึ้นใน PIX หรือเครื่องมือที่คล้ายกันจะมีประโยชน์มากที่นี่

ในที่สุดภูมิปัญญาเก่ายังคงอยู่ที่นี่ - รายละเอียดตรวจสอบว่าจริง ๆ แล้วมันเป็นปัญหาประสิทธิภาพการทำงานสำหรับคุณและจัดการกับมันแล้วไม่ได้มาก่อน สมมติว่าบางสิ่งอาจเป็นปัญหาด้านประสิทธิภาพและการปฏิบัติตามข้อสันนิษฐานนั้นจะก่อให้เกิดความเสี่ยงอย่างมากต่อปัญหาที่ใหญ่กว่าในภายหลัง


4

อ้างอิงจากลิงค์ / บทความที่โพสต์โดย Robert Rouhani:

"รหัสเงื่อนไข (predication) ถูกใช้ในงานสถาปัตยกรรมที่เก่ากว่าเพื่อเลียนแบบการแยกทางที่แท้จริงถ้าคำสั่งที่คอมไพล์แล้วกับสถาปัตยกรรมเหล่านี้ต้องประเมินทั้งที่ถ่ายและไม่ได้ใช้คำสั่งสาขาในชิ้นส่วนทั้งหมดเงื่อนไขของสาขาจะถูกประเมิน คำแนะนำในแต่ละส่วนของสาขาจะต้องตรวจสอบค่าของรหัสเงื่อนไขก่อนที่จะเขียนผลลัพธ์ของพวกเขาเพื่อลงทะเบียนเป็นผลให้คำแนะนำเฉพาะในสาขาที่เขียนเอาท์พุทดังนั้นในสถาปัตยกรรมเหล่านี้สาขาทั้งหมดค่าใช้จ่ายเท่าที่ทั้งสองส่วนของ Branch รวมถึงค่าใช้จ่ายในการประเมินสภาพสาขาควรใช้ Branching ในสถาปัตยกรรมดังกล่าว GPU GeForce FX ซีรีส์ใช้การจำลองแบบรหัสตามเงื่อนไขในตัวประมวลผลแฟรกเมนต์ "

ตามที่แนะนำ mh01 ("การดูแอสเซมบลีที่สร้างขึ้นใน PIX หรือเครื่องมือที่คล้ายกันจะมีประโยชน์มากที่นี่") คุณควรใช้เครื่องมือคอมไพเลอร์เพื่อตรวจสอบผลลัพธ์ จากประสบการณ์ของฉันเครื่องมือ Cg ของ nVidia (Cg ยังคงใช้กันอย่างแพร่หลายในปัจจุบันเนื่องจากความสามารถของแพลตฟอร์มข้าม) ได้แสดงให้เห็นถึงพฤติกรรมที่สมบูรณ์แบบที่กล่าวถึงในย่อหน้ารหัสเงื่อนไข GPU GPU (การคาดการณ์ล่วงหน้า) ดังนั้นโดยไม่คำนึงถึงค่าทริกเกอร์ทั้งสองสาขาจะได้รับการประเมินตามแต่ละส่วนและท้ายที่สุดเท่านั้นที่จะถูกวางลงในเอาต์พุตรีจิสตรี อย่างไรก็ตามเสียเวลาในการคำนวณ เมื่อก่อนฉันคิดว่าการแตกแขนงจะช่วยให้ประสิทธิภาพโดยเฉพาะอย่างยิ่งเพราะทั้งหมดเศษเล็กเศษน้อยใน shader นั้นขึ้นอยู่กับค่าเครื่องแบบที่จะตัดสินใจในสาขาที่เหมาะสม - ที่ไม่ได้เกิดขึ้นตามที่ตั้งใจไว้ ดังนั้นข้อแม้ที่สำคัญที่นี่ (เช่นหลีกเลี่ยง ubershaders - อาจเป็นแหล่งที่ใหญ่ที่สุดของการแตกแขนงนรก)


2

หากคุณยังไม่มีปัญหาด้านประสิทธิภาพนี่เป็นเรื่องปกติ ค่าใช้จ่ายสำหรับการเปรียบเทียบกับค่าคงที่ยังคงเป็นอย่างมากราคาถูก นี่คือการอ่านที่ดีเกี่ยวกับการแยก GPU: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter34.html

ไม่ว่านี่คือตัวอย่างของโค้ดที่จะ preform มากยิ่งกว่าคำสั่ง if (และสามารถอ่านได้ / บำรุงรักษาน้อยกว่า) แต่ก็ยังกำจัดมันได้:

int fx = floor(x);
int y = (fx * y2) + ((1- fx) * y1);
int b = (fx * b2) + ((1 -fx) * b1);

x = 10 * y + b;

โปรดทราบว่าฉันทำสมมติฐานที่ว่า x ถูก จำกัด [0, 1]ให้ช่วง สิ่งนี้จะไม่ทำงานหาก x> = 2 หรือ x <0

สิ่งที่ snipped ทำคือแปลง x เป็นหนึ่ง0หรือ1คูณทวีคูณผิดด้วย 0 และอีกอันหนึ่งคูณ 1


ตั้งแต่การทดสอบเดิมเป็นif(x<0.5)ค่าfxที่ควรจะเป็นหรือround(x) floor(x + 0.5)
sam hocevar

1

มีคำสั่งมากมายที่สามารถทำเงื่อนไขได้โดยไม่ต้องแยกสาขา

vec4 when_eq(vec4 x, vec4 y) {
  return 1.0 - abs(sign(x - y));
}

vec4 when_neq(vec4 x, vec4 y) {
  return abs(sign(x - y));
}

vec4 when_gt(vec4 x, vec4 y) {
  return max(sign(x - y), 0.0);
}

vec4 when_lt(vec4 x, vec4 y) {
  return max(sign(y - x), 0.0);
}

vec4 when_ge(vec4 x, vec4 y) {
  return 1.0 - when_lt(x, y);
}

vec4 when_le(vec4 x, vec4 y) {
  return 1.0 - when_gt(x, y);
}

บวกตัวดำเนินการเชิงตรรกะบางตัว

vec4 and(vec4 a, vec4 b) {
  return a * b;
}

vec4 or(vec4 a, vec4 b) {
  return min(a + b, 1.0);
}

vec4 xor(vec4 a, vec4 b) {
  return (a + b) % 2.0;
}

vec4 not(vec4 a) {
  return 1.0 - a;
}

แหล่งที่มา: http://theorangeduck.com/page/avoiding-shader-conditionals

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.