Objective c 嵌套循环的线程
我一直在寻找一个类似的问题,但没有任何成功。我不知道如何优化cocoa中的一些代码以使用所有可用的CPU核心(我现在不想使用GPU)。下面是简单的代码示例,我的意思是: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++
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);