2015-01-20 3 views
2

Я читал по этому вопросу, но я не смог найти конкретного ответа на мой вопрос. Меня интересует использование параллелизма/многопоточности для улучшения производительности моей игры, но я слышал некоторые противоречащие факты. Например, многопоточность может не улучшить производительность игры. ЯParallelism vs Threading - Performance

Я подумал о двух способов сделать это:

  • положить компонент рендеринга в нить. Есть некоторые вещи Мне нужно будет изменить, но у меня есть хорошее представление о том, что должно быть сделано.
  • с использованием openMP для параллелизации функции рендеринга. У меня уже есть код для этого, поэтому это может быть проще.

Это, как оценка Uni, целевое оборудование - это компьютеры моего Uni, которые являются многоядерными (4 ядра), и поэтому я надеюсь достичь некоторой дополнительной эффективности, используя любой из этих методов.

Мой вопрос, есть поэтому: Какой из них я предпочитаю? Что обычно дает наилучшие результаты?

EDIT: Основная функция, которую я имею в виду распараллелить/многопоточную легко:

void Visualization::ClipTransBlit (int id, Vector2i spritePosition, FrameData frame, View *view) 
{ 
    const Rectangle viewRect = view->GetRect(); 
    BYTE *bufferPtr = view->GetBuffer(); 

    Texture *txt = txtMan_.GetTexture (id); 
    Rectangle clippingRect = Rectangle (0, frame.frameSize.x, 0, frame.frameSize.y); 

    clippingRect.Translate (spritePosition); 
    clippingRect.ClipTo (viewRect); 
    Vector2i negPos (-spritePosition.x, -spritePosition.y); 
    clippingRect.Translate (negPos); 

    if (spritePosition.x < viewRect.left_) { spritePosition.x = viewRect.left_; } 
    if (spritePosition.y < viewRect.top_) { spritePosition.y = viewRect.top_; } 

    if (clippingRect.GetArea() == 0) { return; } 

    //clippingRect.Translate (frameData); 

    BYTE *destPtr = bufferPtr + ((abs(spritePosition.x) - abs(viewRect.left_)) + (abs(spritePosition.y) - abs(viewRect.top_)) * viewRect.Width()) * 4; // corner position of the sprite (top left corner) 
    BYTE *tempSPtr = txt->GetData() + (clippingRect.left_ + clippingRect.top_ * txt->GetSize().x) * 4; 

    int w = clippingRect.Width(); 
    int h = clippingRect.Height(); 
    int endOfLine = (viewRect.Width() - w) * 4; 
    int endOfSourceLine = (txt->GetSize().x - w) * 4; 

    for (int i = 0; i < h; i++) 
    { 
     for (int j = 0; j < w; j++) 
     { 
      if (tempSPtr[3] != 0) 
      { 
       memcpy(destPtr, tempSPtr, 4); 
      } 

      destPtr += 4; 
      tempSPtr += 4; 
     } 

     destPtr += endOfLine; 
     tempSPtr += endOfSourceLine; 
    } 

}

+0

Можете ли вы опубликовать код, который вы рассматриваете как многопоточность? –

+0

Лучшие результаты зависят от того, насколько параллелизуемы части и ваша способность дразнить этот параллелизм, чтобы его можно было использовать. Вы не можете ответить на вопрос, не исследуя код. –

+0

@MichaelB. Он немного длинный, поскольку он включает в себя несколько функций, но если это необходимо, я сделаю ссылку pastebin. – MKII

ответ

2

вместо вызова тетсра для каждого пикселя рассмотреть только установив значение там. накладные расходы при вызове функции, которая во много раз могла доминировать над общим временем выполнения для этого цикла. Например:

for (int i = 0; i < h; i++) 
{ 
    for (int j = 0; j < w; j++) 
    { 
     if (tempSPtr[3] != 0) 
     { 
      *((DWORD*)destPtr) = *((DWORD*)tempSPtr); 
     } 

     destPtr += 4; 
     tempSPtr += 4; 
    } 

    destPtr += endOfLine; 
    tempSPtr += endOfSourceLine; 
} 

можно также избежать условной, используя один из трюков, упомянутых здесь avoiding conditionals - в такой узкой условной петле может быть очень дорогими.

редактировать - , чтобы ли это лучше запускать несколько экземпляров ClipTransBlit одновременно или распараллеливание ClipTransBlit внутри, я бы сказал, вообще говоря, это лучше реализовать распараллеливание на таком высоком уровне, как это возможно, чтобы уменьшить накладные расходы, которые вы понесете (создавая потоки, синхронизируя их и т. д.)

В вашем случае, хотя, похоже, что вы рисуете спрайты, если они должны были накладываться, то без дополнительной синхронизации ваша потоковая обработка высокого уровня может привести к неприятным визуальным эффектам артефактов и даже состояния гонки при проверке альфа-бит. В этом случае параллелизм с низким уровнем может быть лучшим выбором.

+0

Я не вижу никакого способа называть его один раз в строке, не делая что-то еще, что в любом случае снижает производительность. Кроме того, это рендеринг, единственный раз, когда я занимаюсь графикой, это при записи буфера представления в экран. – MKII

+0

@MKII Я не заметил, что вы писали только в dest, когда альфа-байт не равен нулю - вы все равно можете воспользоваться удалением вызова memcpy, например, в редакторе – gordy

+0

Я бы это сделал, но я бы поставил стоимость филиала более дорогая, чем просто запись, которая не будет видна. Если у вас есть большие разделы вашего источника, как известно, содержит альфу, что вы можете реализовать алгоритм, который позволит вам пропускать блики больших секций кадра. – rparolin

0

Теоретически, они должны производить тот же эффект. На практике это может быть совсем иная.

Если вы распечатываете код сборки программы OpenMP, OpenMP просто вызывает некоторую функцию в области, например #pragma omp parallel .... Он похож на folk.

OpenMP ориентирован на параллельные вычисления, с другой стороны, многопоточность является более общей. Например, если вы хотите написать GUI-программу, необходимо многопоточность (некоторые фреймворки могут скрыть ее. По-прежнему требуется несколько потоков). Однако вы никогда не хотите реализовывать его с помощью OpenMP.