-
VGPR, SGPR 사용여부 확인실험실 2020. 9. 24. 01:35
GPU에 대해서 더 공부하고 싶은 차에 [번역] INTRO TO GPU SCALARIZATION – PART 1 글을 봤었고, SGPR/SALU를 사용하는 경우 Wave내에 모든 Thread가 동일한 코드를 사용하게 되어서 성능 향상이 있을 것이라고 했습니다.
사실 이쪽은 완전히 새로운 영역이라... 성능차이가 정말 나는 것인가? 한 번해보고 싶었는데, 현재 Shader 코드가 VGPR/VALU or SGPR/SALU를 사용하는지 여부도 확인하는 법을 몰랐습니다. 최근에 렌더독을 이용하면 이 것을 확인 할 수 있다는 것을 알게 되었고, 실제로 Shader 코드가 사용하는 레지스터를 판별 할 수 있는지 한번 테스트 해봤습니다.
[번역] INTRO TO GPU SCALARIZATION – PART 1에서는 Note[0]에 SALU에는 ISA에 부동소수점 연산 명령어가 없어서 VALU가 될 거라고 하던데, 실제 AMD "Vega" Instruction Set Architecture에 나와있는 것을 확인했습니다.
그리고 렌더독으로 쉐이더 코드를 테스트 해본 결과 float인 경우 SGPR의 데이터를 VGPR에 올린 뒤 VALU로 연산되는 것을 아래 코드로 확인할 수 있습니다. 렌더독에서 Disassembly type을 Vega20으로 하여 디버깅 한 예 입니다.
////////////////////////////////////////////////////////////////////////////////// // 아래 코드가 주석대로 SGPR과 VGPR에 올라가는지 확인 해보자. // // This will be in a SGPR // -> [OK 잘 올라감] float s_value = LightDirection.x; // This will be put in VGPRs via a VMEM load as pixelCoord is in VGPRs // -> [VGPR에 잘 올라감] float4 v_textureSample = ShaderTextures[0].Sample(SamplerType, Input.Tex); // This will be put in SGPRs via a SMEM load as 0 is constant. // -> [SGPR에 잘 올라감] TestInput s_someData = TestStructuredBuffer.Load(0); // This should be an SALU op (output in SGPR) since both operands are in SGPRs // (Note, check note [0]) // -> [여기서 SALU가 사용될 것이라고 했지만 아니었음. SGPR에는 32bit Integer와 // 32 bit or 64 bit Bit연산만 지원해서 VGPR로 옮긴 뒤 VALU로 연산함] // * s_someData.Color.x 는 float임 * float s_someModifier = s_value + s_someData.Color.x; //// This will be a VALU (output in VGPR) since one operand is VGPR. // -> [VGPR에 잘 올라감] float4 v_finalResult = s_someModifier * v_textureSample; return v_finalResult; ////////////////////////////////////////////////////////////////////////////////// ; Disassembly for GCN (Vega 20) ; -------- Statistics --------------------- ; SGPRs: 27 out of 104 used ; VGPRs: 5 out of 256 used ; LDS: 0 out of 65536 bytes used ; 0 bytes scratch space used ; Instructions: 12 ALU, 3 Control Flow, 1 TFETCH ; -------- Disassembly -------------------- ; ****** 각 어셈블리 라인의 우측에 HLSL 코드의 실제 라인을 써뒀으니 참고. ****** shader main asic(GFX9) type(PS) // s_ps_state in s0 s_mov_b32 m0, s24 // 000000000000: BEFC0018 s_mov_b64 s[2:3], exec // 000000000004: BE82017E s_wqm_b64 exec, exec // 000000000008: BEFE077E // 여기서 float4 v_textureSample = ShaderTextures[0].Sample(SamplerType, Input.Tex); v_interp_p1_f32 v2, v0, attr0.x // 00000000000C: D4080000 // v_interp_p1_f32 v3, v0, attr0.y // 000000000010: D40C0100 // v_interp_p2_f32 v2, v1, attr0.x // 000000000014: D4090001 // 이까지 v_interp_p2_f32 v3, v1, attr0.y // 000000000018: D40D0101 image_sample v[0:3], v[2:4], s[4:11], s[16:19] dmask:0xf // 00000000001C: F0800F00 00810002 s_buffer_load_dword s0, s[20:23], 0x10 // 000000000024: C022000A 00000010 // float s_value = LightDirection.x; s_buffer_load_dword s1, s[12:15], 0x00 // 00000000002C: C0220046 00000000 // TestInput s_someData = TestStructuredBuffer.Load(0); s_waitcnt lgkmcnt(0) // 000000000034: BF8CC07F v_mov_b32 v4, s0 // 000000000038: 7E080200 // float s_value = LightDirection.x; v_add_f32 v4, s1, v4 // 00000000003C: 02080801 // float s_someModifier = s_value + s_someData.Color.x; s_waitcnt vmcnt(0) // 000000000040: BF8C0F70 v_mul_f32 v0, v4, v0 // 000000000044: 0A000104 // 여기서 float4 v_finalResult = s_someModifier * v_textureSample; v_mul_f32 v1, v4, v1 // 000000000048: 0A020304 // v_mul_f32 v2, v4, v2 // 00000000004C: 0A040504 // v_mul_f32 v3, v4, v3 // 000000000050: 0A060704 // 이까지 s_mov_b64 exec, s[2:3] // 000000000054: BEFE0102 v_cvt_pkrtz_f16_f32 v0, v0, v1 // 000000000058: D2960000 00020300 v_cvt_pkrtz_f16_f32 v1, v2, v3 // 000000000060: D2960001 00020702 exp mrt0, v0, v0, v1, v1 done compr vm // 000000000068: C4001C0F 00000100 // return v_finalResult; s_endpgm // 000000000070: BF810000 end
그 외에 더 다양한 툴을 프로파일링에 사용할 수 있는데, 대부분 DX12나 Vulkan에서 사용가능한 것 같네요. 특히, DX12 부터는 Wave intrinsics의 사용이 가능합니다. [번역] INTRO TO GPU SCALARIZATION – PART 1 에서 설명하는 나머지 부분은 API를 숙지하고 다시 해봐야겠네요.
추가
StructuredBuffer에 int를 전달하여 SGPR로 ALU연산이 되는지 한번 확인해봤는데, 실제로 그렇게 됩니다.
///////////////////////////////////////////////////////////// TestInput s_someData = TestStructuredBuffer.Load(0); int k = s_someData.Color * s_someData.Color * 2.0; return k; ///////////////////////////////////////////////////////////// ; Disassembly for GCN (Vega 20) ; -------- Statistics --------------------- ; SGPRs: 11 out of 104 used ; VGPRs: 3 out of 256 used ; LDS: 0 out of 65536 bytes used ; 0 bytes scratch space used ; Instructions: 2 ALU, 2 Control Flow, 0 TFETCH ; -------- Disassembly -------------------- shader main asic(GFX9) type(PS) // s_ps_state in s0 s_buffer_load_dword s0, s[4:7], 0x00 // 000000000000: C0220002 00000000 s_waitcnt lgkmcnt(0) // 000000000008: BF8CC07F s_mul_i32 s0, s0, s0 // 00000000000C: 92000000 // int k = s_someData.Color * s_someData.Color 부분에 해당 v_cvt_f32_i32 v0, s0 mul:2 // 000000000010: D1450000 08000000 // 위의 결과에 2를 곱함 v_cvt_pkrtz_f16_f32 v0, v0, v0 // 000000000018: D2960000 00020100 s_nop 0x0000 // 000000000020: BF800000 s_nop 0x0000 // 000000000024: BF800000 exp mrt0, v0, v0, v0, v0 done compr vm // 000000000028: C4001C0F 00000000 s_endpgm // 000000000030: BF810000 end
'실험실' 카테고리의 다른 글
[PBR] Substance/Roughness/Metalic (0) 2021.05.03 BRDF 정리 노트 (작성중) (0) 2020.11.11 Radiometric Quantities (0) 2020.10.26