2016-09-06 45 views
10

Próbuję umieścić symulacji marszczyć na szczycie już bugs aplikacji. W tej chwili procesor działa na około 11 ms na najniższych procesorach. Cały dotychczasowy kod działa na głównym wątku.Optymalizacja kodu bez usuwania blokady wątku

Mam nadzieję, że możliwe jest umieszczenie symulacji marszruty w całości na innym wątku.

Symulacja jest oparta na jabłku GLCameraRipple project. Zasadniczo tworzy prostokąt tesselated i oblicza współrzędne tekstury. Tak więc w idealnym świecie tekstura jest współrzędna, a pulsujące symulacje tablic są na innym wątku.

Funkcja aktualizacji, nad którą pracuję teraz, wygląda następująco. Działa ona w sposób GCD, jednak nie zyskuje na szybkości z powodu synchronizacji. Jednak bez synchronizacji aplikacja zawiesiłaby się, ponieważ szybkie tablice nie są bezpieczne dla wątków.

var rippleTexCoords:[GLfloat] = [] 
var rippleSource:[GLfloat] = [] 
var rippleDest:[GLfloat] = [] 
func runSimulation() 
{ 
    if (firstUpdate) 
    {firstUpdate = false; Whirl.crashLog("First update")} 

    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)  
    let block1: (y: size_t) -> Void = { 
    (y: size_t) -> Void in 

    objc_sync_enter(self) 
    defer { objc_sync_exit(self) } // */ This will actually run at the end 

    let pw = self.poolWidthi 
    for x in 1..<(pw - 1) 
    { 
     let ai:Int = (y ) * (pw + 2) + x + 1 
     let bi:Int = (y + 2) * (pw + 2) + x + 1 
     let ci:Int = (y + 1) * (pw + 2) + x 
     let di:Int = (y + 1) * (pw + 2) + x + 2 
     let me:Int = (y + 1) * (pw + 2) + x + 1 

     let a = self.rippleSource[ai] 
     let b = self.rippleSource[bi] 
     let c = self.rippleSource[ci] 
     let d = self.rippleSource[di] 

     var result = self.rippleDest[me] 
     result = (a + b + c + d)/2.0 - result 
     result -= result/32.0 

     self.rippleDest[me] = result 
    } 
    //Defer goes here 
} 

dispatch_apply(Int(poolHeighti), queue, block1); 

/*for y in 0..<poolHeighti { 
    block1(y: y) 
}*/ 

let hm1 = GLfloat(poolHeight - 1) 
let wm1 = GLfloat(poolWidth - 1) 
let block2: (y: size_t) -> Void = { 
    (y: size_t) -> Void in 

    objc_sync_enter(self) 
    defer { objc_sync_exit(self) } // */ 

    let yy = GLfloat(y) 
    let pw = self.poolWidthi 
    for x in 1..<(pw - 1) { 
     let xx = GLfloat(x) 
     let ai:Int = (y ) * (pw + 2) + x + 1 
     let bi:Int = (y + 2) * (pw + 2) + x + 1 
     let ci:Int = (y + 1) * (pw + 2) + x 
     let di:Int = (y + 1) * (pw + 2) + x + 2 

     let a = self.rippleDest[ai] 
     let b = self.rippleDest[bi] 
     let c = self.rippleDest[ci] 
     let d = self.rippleDest[di] 

     var s_offset = ((b - a)/2048) 
     var t_offset = ((c - d)/2048) 

     s_offset = (s_offset < -0.5) ? -0.5 : s_offset; 
     t_offset = (t_offset < -0.5) ? -0.5 : t_offset; 
     s_offset = (s_offset > 0.5) ? 0.5 : s_offset; 
     t_offset = (t_offset > 0.5) ? 0.5 : t_offset; 

     let s_tc = yy/hm1 
     let t_tc = xx/wm1 

     let me = (y * pw + x) * 2 
     self.rippleTexCoords[me + 0] = s_tc + s_offset 
     self.rippleTexCoords[me + 1] = t_tc + t_offset 

    } 
} 
     dispatch_apply(poolHeighti, queue, block2) 

     /* for y in 0..<poolHeighti { 
     block2(y: y) 
     } */// 

     let pTmp = rippleDest 
     rippleDest = rippleSource 
     rippleSource = pTmp 
    } 

Czy istnieje sposób na wymuszenie ciągłego uruchamiania tego kodu na innym wątku? Albo jakoś go przyspieszyć?

dont konw czy to możliwe, ale jeśli jest to musiałbym te tablice na temat wątku:

główne:

  • rippleVertices
  • rippleIndices

wtórne: (Nie są one odczytywane ani zapisywane w głównym wątku)

  • rippleSource
  • rippleDest

na obu wątków:

  • rippleTexCoords (czytaj na głównym wątku, napisane na wątku wtórny)

Jeśli te warunki są przestrzegane wtedy runSimulation metoda może być uruchomiona na drugim wątku bez problemu.

Odpowiedz

5

W dzisiejszych czasach pamięć jest tania. Dlaczego nie zapisać wyniku w dodatkowej tablicy pracy? Dostęp tylko do odczytu do rippleDest i rippleSource nie wymaga synchronizacji. Będziesz musiał użyć blokady tylko podczas kopiowania obliczonych wyników do rippleDest, redukując w ten sposób czas blokowania do absolutnego minimum.

Dla innych przyrostów prędkości, zacznę od przeniesienia inicjalizacji wszystkich indeksów ai, bi, ci, di, me, z pętli, ponieważ są one inkrementowane o 1 dla każdej iteracji. Pozwoli to zaoszczędzić co najmniej pół tuzina operacji na węzeł nawet po optymalizacji kompilatora - tyle operacji, ile użytecznych prac wykonanych przez procedurę. Prawdopodobnie nie dostaniesz 50% poprawy, ale coś bliżej 10-15%, co nie jest złe.

+0

To jest naprawdę genialny pomysł! Nie wiem dokładnie, jak to osiągnąć, ale będę się z tym bawić. Dziękuję Ci! –

+0

Być może możesz użyć semafora, aby rozpocząć obliczanie tętnienia, a następnie ustawić flagę, gdy to zrobisz. Następnie sprawdź/poczekaj na flagę przed renderowaniem z głównego wątku. Tego rodzaju rozwiązanie wyeliminowałoby konieczność użycia kopii roboczej w ogóle. –

2

To obj_sync zapobiega uruchomieniu twojego kodu na wielu wątkach, więc dispatch_apply po prostu spowolni działanie.

+0

Zgadzam się, a ja w pewnym stopniu wspominam o tym w moim pytaniu, chociaż nie używam dokładnych informacji.Tematem tego pytania jest poprawa szybkości kodu, aby dopasować to, co można zrobić w obiektywnym celu - c –