Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/macos/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading OSXGCD多线程并发使用更多CPU,但执行速度比单线程慢_Multithreading_Macos_Concurrency_Swift_Grand Central Dispatch - Fatal编程技术网

Multithreading OSXGCD多线程并发使用更多CPU,但执行速度比单线程慢

Multithreading OSXGCD多线程并发使用更多CPU,但执行速度比单线程慢,multithreading,macos,concurrency,swift,grand-central-dispatch,Multithreading,Macos,Concurrency,Swift,Grand Central Dispatch,我有一种方法,可以做一系列的计算,需要花费相当多的时间才能完成。此方法对其进行计算的对象是在运行时生成的,范围从几个到几千个。显然,如果我能在多个线程上同时运行这些计算会更好,但当我尝试这样做时,我的程序使用了更多的CPU,但要比逐个运行它们花费更长的时间。你知道为什么吗 let itemsPerThread = (dataArray.count / 4) + 1 for var i = 0; i < dataArray.count; i += itemsPerThread {

我有一种方法,可以做一系列的计算,需要花费相当多的时间才能完成。此方法对其进行计算的对象是在运行时生成的,范围从几个到几千个。显然,如果我能在多个线程上同时运行这些计算会更好,但当我尝试这样做时,我的程序使用了更多的CPU,但要比逐个运行它们花费更长的时间。你知道为什么吗

let itemsPerThread = (dataArray.count / 4) + 1

for var i = 0; i < dataArray.count; i += itemsPerThread
{

    let name = "ComputationQueue\(i)".bridgeToObjectiveC().cString()
    let compQueue = dispatch_queue_create(name, DISPATCH_QUEUE_CONCURRENT)
    dispatch_async(compQueue,
    {
        let itemCount = i + itemsPerThread < dataArray.count ? itemsPerThread : dataArray.count - i - 1

        let subArray = dataArray.bridgeToObjectiveC().subarrayWithRange(NSMakeRange(i, dataCount)) as MyItem[]
        self.reallyLongComputation(subArray, increment: increment, outputIndex: self.runningThreads-1)
    })

    NSThread.sleepForTimeInterval(1)
}
让itemsPerThread=(dataArray.count/4)+1
对于var i=0;i
或者: 如果我运行相同的东西,但是一个
dispatch\u async
调用,并且总体上
dataArray
而不是子阵列,那么它完成得更快,同时使用更少的CPU。

您需要

  • 摆脱1秒钟的睡眠。这是人为地降低了并行执行的程度,因为在启动下一个线程之前需要等待。您正在启动4个线程,因此您人为地将最后一个线程的开始(可能是结束)延迟了3秒
  • 使用单个并发队列,而不是每个调度块使用一个。并发队列将按照调度块的顺序启动块,但不会等到一个块完成后再启动下一个块,即并行运行块
  • NSArray是一个线程安全类。我假设它在内部使用了多读/单写器锁,这意味着创建一组子数组可能没有好处。但是,您将承担创建子阵列的开销
  • 运行在不同内核上的多个线程不能同时与同一缓存线通信。典型的缓存线大小为64字节,这似乎不太可能在这里造成问题 你(我猜)想做的应该是这样的

    //
    //  main.swift
    //  test
    //
    //  Created by user3441734 on 12/11/15.
    //  Copyright © 2015 user3441734. All rights reserved.
    //
    
    import Foundation
    
    let computationGroup = dispatch_group_create()
    
    var arr: Array<Int> = []
    for i in 0..<48 {
        arr.append(i)
    }
    print("arr \(arr)")
    
    func job(inout arr: Array<Int>, workers: Int) {
        let count = arr.count
        let chunk = count / workers
        guard chunk * workers == count else {
            print("array.cout divided by workers must by integer !!!")
            return
        }
        let compQueue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT)
        let syncQueue = dispatch_queue_create("aupdate", DISPATCH_QUEUE_SERIAL)
    
        for var i = 0; i < count; i += chunk
        {
            let j = i
            var tarr = arr[j..<j+chunk]
            dispatch_group_enter(computationGroup)
            dispatch_async(compQueue) {  () -> Void in
                for k in j..<j+chunk {
                    // long time computation
                    var z = 100000000
                    repeat {
                        z--
                    } while z > 0
                    // update with chunk
                    tarr[k] = j
                }
                dispatch_async(syncQueue, { () -> Void in
                    for k in j..<j+chunk {
                        arr[k] = tarr[k]
                    }
                    dispatch_group_leave(computationGroup)
                })
    
            }
        }
        dispatch_group_wait(computationGroup, DISPATCH_TIME_FOREVER)
    }
    var stamp: Double {
        return NSDate.timeIntervalSinceReferenceDate()
    }
    
    print("running on dual core ...\n")
    var start = stamp
    job(&arr, workers: 1)
    print("job done by 1 worker in \(stamp-start) seconds")
    print("arr \(arr)\n")
    
    start = stamp
    job(&arr, workers: 2)
    print("job done by 2 workers in \(stamp-start) seconds")
    print("arr \(arr)\n")
    
    start = stamp
    job(&arr, workers: 4)
    print("job done by 4 workers in \(stamp-start) seconds")
    print("arr \(arr)\n")
    
    start = stamp
    job(&arr, workers: 6)
    print("job done by 6 workers in \(stamp-start) seconds")
    print("arr \(arr)\n")
    
    。。。您可以使用next模式在任意数量的工作人员之间分配作业(提供最佳性能的工作人员数量取决于工作人员定义和环境中可用的源)。通常,对于任何类型的长时间计算(转换),您都可以期望获得一些性能增益。在两个核心环境中,高达50%。如果您的工作人员“默认”使用使用更多内核的高度优化功能,那么性能增益可能几乎为零:-)

    //通用实现
    //1)在工人之间尽可能公平地分配工作数据
    //2)工人并行完成任务
    //3)结果数组中的顺序反映了输入数组
    //4)无工人区要求返回
    //与您的“计算”结果相同的类型
    func作业(arr:[T],worker:Int,worker:T->U)->[U]{
    警卫工作人员>0其他{return[U]()}
    var res:Dictionary=[:]
    让workersQueue=调度队列队列队列队列队列创建(“工人”,调度队列队列队列并发)
    让syncQueue=dispatch\u queue\u create(“sync”,dispatch\u queue\u SERIAL)
    let group=dispatch\u group\u create()
    var j=最小值(工人,arr.count)
    变量i=(0,0,arr.count)
    变量块:ArraySlice=[]
    重复{
    设a=(i.1,i.1+i.2/j,i.2-i.2/j)
    i=a
    chunk=arr[i.0..]英寸
    results.appendContentsOf(res[idx]!)
    }
    返回结果
    }
    
    为什么跨多个线程运行计算会使用较少的CPU???@HotLicks,因为出于某种原因,它比单线程和CPU密集度较低的替代方案要慢得多…多线程不是万灵药——在添加多线程后让程序减速是很常见的,并且可能需要一些额外的重新组织我想多线程和更高的CPU使用率都意味着它正在快速发展。子阵列中的每项计算通常需要4-9秒,子阵列可以包含1到1000个数据项等待1秒钟是为了修复另一个bug(我现在忘记了)。至于多个队列,据我所知,并发队列彼此并行运行,而不是在单个队列中进行计算。否-调度队列将按顺序启动块,但只有串行队列将在启动下一个块之前等待一个块完成。并发队列可以自由使用尽可能多的线程正如它所希望的,并且不能保证块按启动顺序完成。所有队列都可以彼此并行运行,包括串行队列。@aAirsource有限公司,Garret代码的设计应该反映可用的源代码。您上面的所有答案都是对的(我不太了解NSArray).最后一句话对Garret的设计没有影响。Garret的设计完全是错误的…不是背后的基本思想。如果他至少有两个内核可用,他可以通过适当的设计来减少计算时间
    arr [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]
    running on dual core ...
    
    job done by 1 worker in 5.16312199831009 seconds
    arr [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    
    job done by 2 workers in 2.49235796928406 seconds
    arr [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24]
    
    job done by 4 workers in 3.18479603528976 seconds
    arr [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36]
    
    job done by 6 workers in 2.51704299449921 seconds
    arr [0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40]
    
    Program ended with exit code: 0
    
    // generic implementation
    // 1) job distribute data between workers as fair, as possible
    // 2) workers do their task in parallel
    // 3) the order in resulting array reflect the input array
    // 4) there is no requiremets of worker block, to return
    //    the same type as result of yor 'calculation'
    
    func job<T,U>(arr: [T], workers: Int, worker: T->U)->[U] {
    
        guard workers > 0 else { return [U]() }
    
        var res: Dictionary<Int,[U]> = [:]
    
        let workersQueue = dispatch_queue_create("workers", DISPATCH_QUEUE_CONCURRENT)
        let syncQueue = dispatch_queue_create("sync", DISPATCH_QUEUE_SERIAL)
        let group = dispatch_group_create()
    
        var j = min(workers, arr.count)
        var i = (0, 0, arr.count)
        var chunk: ArraySlice<T> = []
    
        repeat {
            let a = (i.1, i.1 + i.2 / j, i.2 - i.2 / j)
            i = a
            chunk = arr[i.0..<i.1]
            dispatch_group_async(group, workersQueue) { [i, chunk] in
                let arrs = chunk.map{ worker($0) }
                dispatch_sync(syncQueue) {[i,arrs] in
                    res[i.0] = arrs
                }
            }
            j--
        } while j != 0
    
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
        let idx = res.keys.sort()
        var results = [U]()
        idx.forEach { (idx) -> () in
            results.appendContentsOf(res[idx]!)
        }
        return results
    }