2011-02-06 14 views
20

나는 조건부로 일부 정점을 삭제하고자하는 버텍스 쉐이더에서 일하고 있어요 :셰이더 최적화 : 삼항 연산자가 분기와 동일한가요?

float visible = texture(VisibleTexture, index).x; 
if (visible > threshold) 
    gl_Vertex.z = 9999; // send out of frustum 

나는 이웃 데이터 사이의 작은 공통점이있을 때 가지 성능을 죽일 것을 알고있다. 이 경우, 다른 모든 버텍스는 서로 다른 'visible'값을 가질 수 있습니다. 이는 내 이해에 따라 로컬 쉐이더 코어 클러스터의 성능에 좋지 않습니다.

내 질문에 : (가독성 문제와 관계없이) 3 항 연산자가 더 좋습니까?

float visible = texture(VisibleTexture, index).x; 
gl_Vertex.z = (visible > threshold) ? 9999 : gl_Vertex.z; 

그렇지 않다면 계산할 가치가 있습니까?

float visible = texture(VisibleTexture, index).x; 
visible = sign(visible - threshold) * .5 + .5; // 1=visible, 0=invisible 
gl_Vertex.z += 9999 * visible; // original value only for visible 

기하학 쉐이더에 의존하지 않고 정점을 드롭하는 더 좋은 방법이 있습니까?

미리 도움을 청하십시오!

답변

7

3 진 연산자는 if 문에 대한 구문 식 설탕입니다. 그들은 동일합니다.

if 문 내부에 더 많은 내용을 작성했다면 여기에서 수행 할 수있는 최적화가있을 수 있지만 분기 중 너무 적은 내부에는 실제로 최적화 할 것이 없습니다.

종종 분기는 기본적으로 사용되지 않습니다.

경우에 따라 삼항 연산자 (또는 if 문)가 조건의 양쪽을 먼저 평가 한 다음 해당 조건에서 만족하지 않는 분기를 무시하는 것일 수 있습니다.

브랜칭을 사용하려면 GPU에 브랜치를 실제로 시도하도록 지시하는 어셈블리를 생성하려면 (GPU가 브랜칭을 지원하는 경우) 셰이더 코드에 분기 컴파일러 플래그를 설정해야합니다. 이 경우 GPU는 분기 예측기에서 사전 정의 된 수의 코어가 분기 중 하나를 차지할 것이라고 판단하는 경우에만 분기를 시도합니다.

귀하의 마일리지는 컴파일러와 GPU에 따라 다를 수 있습니다.

+0

정보 주셔서 감사합니다! – sharoz

+3

삼항 연산자는 구문론적인 설탕이 아닙니다. 적어도 x86에서는 분기 예측과 함께 파이프 라이닝 최적화를 수행합니다 (여기에서 OP를 유용하게 활용할 수 있습니다). 나는 이것이 GPU가 아니라 CPU라는 것을 알고 있지만, 언급 할 만하다고 느꼈다. – GraphicsMuncher

+1

컴파일러는 똑똑합니다. – Olhovsky

0

제 생각에는 이것을 최적화 할 방법이 없습니다. 컴파일러는 여전히 분기 할 수밖에 없으며, 각 제안은 기능적으로 동일합니다.

+2

컴파일러가 분기하지 않고 프로세서가 분기합니다. 또한 GPU에는 조건문을 평가하는 데 최소한 두 가지 방법이 있습니다.가지의 양쪽을 평가하고 한쪽면만을 평가하는 실제 분기를 버립니다. – Olhovsky

+0

그래, 그저 상식적 인거야. – jakev

+0

@TheBigO 어때 "컴파일러는 여전히 프로세서에 대한 분기를 생성하도록 강제됩니다" – jakev

8

이 수학적 솔루션은 조건문을 대체하는 데 사용할 수 있습니다. 이것은 또한 내가 당신의 상황에서이를 구현하는 방법에 대한 그러나 확실하지 오전 bitselect(condition, falsereturnvalue, truereturnvalue);

int a = in0[i], b = in1[i]; 
int cmp = a < b; //if TRUE, cmp has all bits 1, if FALSE all bits 0 
// & bitwise AND 
// | bitwise OR 
// ~ flips all bits 
out[i] = (a&cmp) | (b&~cmp); //a when TRUE and b when FALSE 

으로 OpenCL을 구현한다, 나는 완전히 당신의 코드를 이해하지 확신 해요,하지만 난 도움이 될이 대답을 공급 희망한다, 또는 다른 사람.

+0

매우 훌륭하고 우아한 솔루션! –

+0

이것은 내 신용이 아닙니다. 나는 근원을 추가하지 않았지만 이제 나는 그것이 어디 있는지를 더 이상 알지 못한다는 것을 두려워한다. 그래서 아이디어의 창시자에게 명성을 전합니다. – Mnescat

1

대답은 세 가지에 달려있다 : 그것은

  • 아키텍처 및 언어를 수행하는 최적화의 종류

    1. 컴파일러와
    2. 당신이 삼항 연산자를 사용하는 정확한 상황.이 경우

      int a = condition ? 100 : 0; 
      

      는, 일반적인 아키텍처에 대한 일반적인 컴파일러는 정수로 표현되는 논리 값을 가정 지점을 제거 할 수 있습니다

  • 이 예를 생각해 보자. 코드는 최적화의 같은 종류의 동등한 if 조건으로 할 수 있습니다

    int a = condition * 100 
    

    로 번역 될 수있다 :

    int a = 0; 
    
    if (condition) { 
        a = 100; 
    } 
    

    그것은 모든 컴파일러에 의해 수행 특정 최적화에 따라 달라집니다.

    일반적으로 말하면, 제 조언은 다음과 같습니다. 삼항 연산자를 사용할 수 있다면 그것을 사용하는 것이 좋습니다. 컴파일러에 의해 최적화 될 가능성이 더 큽니다. 또한 선언적 스타일의 코드를 생성합니다.

    +0

    곱셈으로 변환되는 삼항 결과 할당에 대해 : 나는 최적화가 이루어질 수 있다는 점을 이해하지만, 특히 좋지 않은 예라고 생각합니다. 곱셈은 ​​모든 프로세서에 대한 할당 및 분기 조합보다 훨씬 느립니다. 어떤 컴파일러도 실제로는 비슷한 것을 생성하지 않기를 바랍니다. – user1167662

    +1

    @ user1167662 그것은 마이크로 아키텍처에 따라 다르다. 그래픽을 위해 최적화 된 프로세서에서 곱셈은 두 사이클 이상되지 않습니다. 지점은 파이프 라인의 깊이에 따라 비용이 많이들 것입니다. 몇 가지 일반적인 수치는 곱셈을위한 3 사이클, 파이프 라인의 약 14 스테이지의 v/s – HRJ

    4

    사실 이것은 하나의 셰이더 언어에 따라 다릅니다.

    • HLSL과 Cg에서 삼항 연산자는 절대로 분기를 유도하지 않습니다. 대신에 두 가지 가능한 결과가 항상 평가되고 사용되지 않은 것은 이 삭제됩니다. HLSL documentation 인용하려면 : &의 단락 회로 평가와는 달리

      을 &, ||, 그리고 : C에서, HLSL 식 결코 단락 회로가 벡터 연산이기 때문에 평가. 표현의 모든 측면은 항상 평가됩니다.

      Cg의 경우도 마찬가지이며, 여기에서도 삼항 조건부 연산자는 벡터 연산자입니다. (documentation) : C 달리

      은 제 2 및 제 3 피연산자의 식에 부작용없이 항상 조건의 실행된다.

    • ESSL과 GLSL에서 삼항 연산자는 항상 분기로 연결됩니다. 벡터 연산자가 아니므로 조건은 부울로 평가됩니다. GLSL specification :

      세 개의 표현식 (exp1? exp2 : exp3)으로 작동합니다. 이 연산자는 첫 번째 표현식을 평가합니다. 첫 번째 표현식은 스칼라 부울을 가져와야합니다. 결과가 true이면 은 두 번째 표현식을 평가하도록 선택하고, 그렇지 않으면 세 번째 표현식을 평가하도록 선택합니다. 두 번째 및 세 번째 식 중 하나만 계산됩니다.

      (Source for ESSL)

    차이의 그림 인스턴스의 Khronos WebGL test site for ternary operators 사용할 수있다.