在不删除线程锁的情况下优化代码

时间:2016-09-06 04:16:16

标签: ios swift multithreading grand-central-dispatch

我正试图在一个已经很快的应用程序上放置一个波纹模拟。现在cpu在最低处理器上运行大约11ms。到目前为止,它中的所有代码都在主线程上运行。

我希望可以将纹波模拟完全放在另一个线程上。

模拟基于苹果GLCameraRipple project。基本上它会创建一个镶嵌的矩形,并计算纹理坐标。因此,在理想的世界中,纹理坐标和纹波模拟数组都将位于不同的线程上。

我正在使用的更新功能现在看起来像这样。它确实可以利用GCD,但由于同步,它不会因此而获得速度。如果没有同步,应用程序将崩溃,因为swift数组不是线程安全的。

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
    } 

有没有办法强制此代码不断在不同的线程上运行?或者以某种方式让它变得更快?

我不知道是否有可能,但如果是,我会在下面的线程上有这些数组:

主:

  • rippleVertices
  • rippleIndices

辅助:(这些永远不会在主线程上读取或写入)

  • rippleSource
  • rippleDest

在两个主题上:

  • rippleTexCoords(在主线程上阅读,写在辅助线程上)

如果遵循这些条件,则可以在第二个线程上运行runSimulation方法而不会出现问题。

2 个答案:

答案 0 :(得分:5)

这些天记忆力很便宜。为什么不将结果保存在额外的工作数组中?对rippleDest和rippleSource的只读访问不需要同步。在将计算结果复制到rippleDest时,您只需要使用锁定,从而将锁定时间减少到最低限度。

对于其他速度增益,我首先将所有索引ai,bi,ci,di,me的初始化移出循环,因为它们仅在每次迭代时递增1。即使在编译器优化之后,这也将为每个节点节省至少六打操作 - 这与该过程完成的有用工作一样多。你可能不会从中获得50%的提升,但接近10-15%,这还不错。

答案 1 :(得分:2)

obj_sync阻止您的代码在多个线程上运行,因此dispatch_apply只会减慢速度。