Objective c 嵌套循环的线程

Objective c 嵌套循环的线程,objective-c,multithreading,Objective C,Multithreading,我一直在寻找一个类似的问题,但没有任何成功。我不知道如何优化cocoa中的一些代码以使用所有可用的CPU核心(我现在不想使用GPU)。下面是简单的代码示例,我的意思是: int limA = 1000; int limB = 1000; unsigned short tmp; for (int i = 0; i < 10000; i++) { for (int a = 0; a < limA; a++) { for (int b = 0; b < limB; b++

我一直在寻找一个类似的问题,但没有任何成功。我不知道如何优化cocoa中的一些代码以使用所有可用的CPU核心(我现在不想使用GPU)。下面是简单的代码示例,我的意思是:

int limA = 1000;
int limB = 1000;
unsigned short tmp;
for (int i = 0; i < 10000; i++) {
  for (int a = 0; a < limA; a++) {
    for (int b = 0; b < limB; b++) {
      tmp = [[array objectAtIndex:(a*b)] unsignedShortValue];
      c_array[a*limB+b] += tmp;
    }
  }
}
int limA=1000;
int-limB=1000;
无符号短tmp;
对于(int i=0;i<10000;i++){
对于(int a=0;a
假设数组和c_数组已正确初始化等。。。但正如您所看到的,如果我们有许多迭代(在本例中为10^10),那么执行此代码需要一些时间。我认为在几个线程中执行这段代码可能很简单,但是如何同步c_数组呢?在objective-c中,提高此类代码执行时间的最佳方法是什么?也许可以这样做,大多数外部for循环的迭代0-2499将在线程1和2500-4999线程2中执行,等等?我知道这是愚蠢的方式,但我不需要“实时”性能。。。有什么想法吗?

一些建议:

对阵列执行初始传递,以从其对象包装器中提取所有短裤:

short *tmp_array = calloc(limA * limB, sizeof(short));
int tmp_idx = 0;
for (NSNumber *num in array) {
    tmp_array[tmp_idx++] = [num unsignedShortValue];
}
这有几个好处。从10^10方法调用变为10^6,内部循环对编译器不再不透明(它无法“看穿”方法调用),内部循环变得更小,更可能适合指令缓存

尝试将访问模式线性化。现在您正在进行“跨步”访问,因为索引每次都会成倍增加。如果您可以重新排列
tmp_数组中的数据,使按顺序处理的元素在数组中也是按顺序处理的,那么您应该会获得更好的性能(因为对数组的每次访问都会加载一个完整的缓存线,在大多数处理器上为64字节)

从并行性中获得好处可能很棘手。您可以尝试将外部环路替换为:

dispatch_apply(10000, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {

});
OSAtomicAdd
一起在内部循环中使用+=但我怀疑您的速度无论如何都会被内存访问所控制,在混合中添加更多处理器只会导致它们相互攻击(即处理器0加载
c_阵列[1500]
以便它知道要添加什么
tmp
,tmp实际上加载了覆盖[1500-1531]的缓存线,然后处理器1写入
c_阵列[1512]
,使整个缓存线无效并强制重新读取)。此外,我非常确定您需要在c_数组中存储32位值才能做到这一点,因为您将使用OSAtomicAdd32(没有OSAtomicAdd16)

至少,如果您要并行化,那么您需要找出如何将工作划分为32个
c_array
(即64字节)元素的非重叠块,以便避免争用。划分数组的范围还可以避免使用原子添加操作

(编辑)


查看an0的答案,了解一些关于并行化的实用建议,而不是关于为什么简单的并行化不起作用的讨论:)

首先,遵循@Catfish\u Man的建议,除了并行化部分

对于平行性,我的想法如下:

  • 最外面的循环是没有意义的。只需使用
    10000*tmp
    而不是
    tmp
  • 由于要写入的目标数组段对于不同的
    a
    值是严格不相交的,因此第二级循环可以很容易地并行化。事实上,它也适用于
    b
    。但是,如果我们也在
    b
    上进行并行化,那么留在主体中的计算单元将太小,无法有效地拆分工作负载 代码:

    int limA=1000;
    int-limB=1000;
    short*tmp_数组=calloc(limA*limB,sizeof(short));
    int tmp_idx=0;
    for(数组中的NSNumber*num){
    tmp_数组[tmp_idx++]=[num unsignedShortValue];
    }
    调度应用(limA,调度获取全局队列(调度队列优先级默认为0),^(大小){
    for(int b=0;b
    首先,遵循@Catfish\u Man的建议。然后遵循@an0的建议。然后也要这样做:

    /。。。
    short*tmp_数组=calloc(limA*limB,sizeof(short));
    unsigned short(*unsignedShortValueIMP)(id,SEL)=class_getMethodImplementation([NSNumber class],@selector(unsignedShortValue));
    void*(*objectAtIndexIMP)(id,SEL,NSUInteger)=类_getMethodImplementation(array.class,@selector(objectAtIndex:);
    整数n=array.count;
    对于(整数i=0;i
    通过将
    IMP
    s从Objective-C中提升出来,您可以绕过消息分派机制的所有开销,并允许编译器“看穿”调用;虽然这些选择器是基础的一部分,并且不能被内联,但是移除额外的间接级别提高了CPU内核中的分支预测和预取机制的神圣性。此外,通过使用原始C for循环而不是Objective-C的数组枚举,并且不强制编译器上objc_msgSend()的不透明性,可以让Clang的循环展开和向量化优化器工作

    @Catfish_-Man可能会告诉我,这是一个过时的优化,不再值得去做,但据我所知,这仍然是一个胜利,因为大量重复调用像这样的方法

    最后一点注意:我的代码假定为圆弧,因此在
    objectAtIndex:
    上使用
    void*
    和桥接,而不是
    id
    ,以绕过额外的隐式
    保留
    释放
    对。这是邪恶的影子
    int limA = 1000;
    int limB = 1000;
    short *tmp_array = calloc(limA * limB, sizeof(short));
    int tmp_idx = 0;
    for (NSNumber *num in array) {
        tmp_array[tmp_idx++] = [num unsignedShortValue];
    }
    dispatch_apply(limA, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t a) {
        for (int b = 0; b < limB; b++) {
            tmp = ;
            c_array[a*limB+b] += 1000 * tmp_array[a*b];
        }
    });
    free(tmp_array);