Оптимизировать код без удаления блокировки потока

Я пытаюсь запустить симуляцию пульсации уже готового приложения. Сейчас процессор работает примерно на 11 мс на самых низких процессорах. Весь код в нем до сих пор работает на основном потоке.

Я надеюсь, что можно моделировать пульсацию целиком на другой поток.

Моделирование основано на проекте GLCameraRipple. В основном он создает тесселированный прямоугольник и вычисляет координаты текстуры. Таким образом, в идеальном мире координаты текстуры, а рябь, имитирующая массивы, все будет в другом потоке.

Функция обновления, с которой я сейчас работаю, выглядит следующим образом. Он использует сортировку GCD, но не получает от нее никакой скорости из-за синхронизации. Без синхронизации, однако, приложение потерпит крах, потому что быстрые массивы не являются потокобезопасными.

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
    } 

Есть ли способ заставить этот код постоянно работать в другом потоке? Или как-нибудь заставить его идти быстрее?

Я не знаю, если бы это было возможно, но если бы это было, я бы имел эти массивы в следующем потоке:

Main:

  • rippleVertices
  • rippleIndices

Вторичный: (они никогда не читаются или не записываются в основной поток)

  • rippleSource
  • rippleDest

В обоих потоках:

  • rippleTexCoords (читайте в основном потоке, написанном на вторичном потоке)

Если эти условия соблюдены, тогда метод runSimulation может быть запущен во втором потоке без проблем.

Ответ 1

В наши дни память грязная. Почему бы не сохранить результат в дополнительном массиве работы? Доступ только для чтения к rippleDest и rippleSource не потребует синхронизации. Вам нужно будет использовать блокировку при копировании вычисленных результатов в rippleDest, тем самым сокращая время блокировки до минимального минимума.

Для получения других скоростей я начну с переноса инициализации всех индексов ai, bi, ci, di, me, из цикла, так как они только увеличиваются на 1 для каждой итерации. Это позволило бы сэкономить не менее полутора десятков операций на node даже после оптимизации компилятора, - столько операций, сколько полезной работы, выполняемой этой процедурой. Вероятно, вы не получите 50% -ного улучшения от этого, но что-то ближе к 10-15%, что неплохо.

Ответ 2

Это obj_sync предотвращает запуск вашего кода на нескольких потоках, поэтому dispatch_apply просто замедляет работу.