Obj-C 方法调用超慢,更多 CPU 用法?
Obj-C Method calls super slow, lots more CPU usage?
我正在进行像素处理,当我调用一个空的 Objective-C 方法时,它多使用了 25% CPU。
此外,另一个奇怪的情况是,如果我在该方法中有另一个 Obj-C 函数调用从未被调用,它会使 CPU 使用量额外增加 10%。
这是我调用的代码:
- (void)addLeftCorner:(struct Position)p top:(BOOL)top {
if (top) {
[blackBorderLock lock];
[topLeftAllyMinionCorners addObject:[NSValue valueWithBytes:&p objCType:@encode(struct Position)]];
[blackBorderLock unlock];
} else {
[blackBorderLock lock];
[bottomLeftAllyMinionCorners addObject:[NSValue valueWithBytes:&p objCType:@encode(struct Position)]];
[blackBorderLock unlock];
}
}
- (void) processPixel:(uint8_t *)pixel x:(int)x y:(int)y{
//Assume multithreaded
if (pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) {
//Check if top left border
if (x < imageData.imageWidth-1) {
if (y < imageData.imageHeight-1) { //Check for top left bar
//Check bottom right pixel
uint8_t *bottomRightPixel = pixel + (imageData.imageWidth + 1)*4;
if (bottomRightPixel[2] == 81 && bottomRightPixel[1] == 162 && bottomRightPixel[0] == 230) {
uint8_t *rightPixel = pixel + (1)*4;
if (rightPixel[0] == 0 && rightPixel[1] == 0 && rightPixel[2] == 0) {
uint8_t *bottomPixel = pixel + (imageData.imageWidth)*4;
if (bottomPixel[0] == 0 && bottomPixel[1] == 0 && bottomPixel[2] == 0) {
struct Position p;p.x=x;p.y=y;
[self addLeftCorner:p top:true];
}
}
}
}
if (y > 0) { //Check for bottom left bar
//Check top right pixel
uint8_t *topRightPixel = pixel + (-imageData.imageWidth + 1)*4;
if (topRightPixel[2] == 40 && topRightPixel[1] == 80 && topRightPixel[0] == 114) {
uint8_t *rightPixel = pixel + (1)*4;
if (rightPixel[0] == 0 && rightPixel[1] == 0 && rightPixel[2] == 0) {
uint8_t *topPixel = pixel - (imageData.imageWidth)*4;
if (topPixel[0] == 0 && topPixel[1] == 0 && topPixel[2] == 0) {
struct Position p;p.x=x;p.y=y;
[self addLeftCorner:p top:false];
}
}
}
}
}
}
}
在 运行 中,addLeftCorner 永远不会被调用,如果我将其注释掉,我将获得额外的 10% CPU 使用率。此外,仅调用方法 processPixel 就占用了 25% CPU 的使用量。
这是为什么?有没有办法优化它?我想拿回那 35% CPU。
TL;DR - 您将需要研究使用 NSObject -methodForSelector: 作为避免 Objective-C 运行时开销的方法。明智地使用它。
-
有一些来自 Smalltalk 的术语对于保持真实情况的心理模型很有用。我们通常指的是 invoking 一个 method 而不是调用一个函数来识别像 Objective-C 这样的动态语言在运行时解析函数地址,每次,响应消息接收者的那种对象。
这种动态调度是动态语言多态性的核心。虽然 Objective-C 运行时几乎达到超人的长度以使方法调用尽可能高效,但它永远不会像直接函数调用那样高效。
大多数时候,方法调用效率低下并不重要。人与应用程序的交互淹没了通常很小的效率损失。但是对于像图像处理这样的计算内核,低效率将变得明显。
通过使用 NSObject -methodForSelector:,您可以将方法解析为函数地址一次,然后跳过运行时查找。仔细研究文档。确保您了解 SEL is and what an IMP 是什么。将方法作为函数调用需要两个额外的参数,self 和 _cmd。确保您了解它们是什么以及如何使用它们。
最重要的是,确保分析绝对证明像这样绕过运行时是必要的。根据您对问题的描述,我不完全相信您看到的正是问题所在。但您是最了解您的软件的人。
你将防止你正在使用这个 method/function 的对象的多态性的可能性,所以它有点具体地设置你设计的这个方面,没有动态语言通常的灵活性.
我正在进行像素处理,当我调用一个空的 Objective-C 方法时,它多使用了 25% CPU。
此外,另一个奇怪的情况是,如果我在该方法中有另一个 Obj-C 函数调用从未被调用,它会使 CPU 使用量额外增加 10%。
这是我调用的代码:
- (void)addLeftCorner:(struct Position)p top:(BOOL)top {
if (top) {
[blackBorderLock lock];
[topLeftAllyMinionCorners addObject:[NSValue valueWithBytes:&p objCType:@encode(struct Position)]];
[blackBorderLock unlock];
} else {
[blackBorderLock lock];
[bottomLeftAllyMinionCorners addObject:[NSValue valueWithBytes:&p objCType:@encode(struct Position)]];
[blackBorderLock unlock];
}
}
- (void) processPixel:(uint8_t *)pixel x:(int)x y:(int)y{
//Assume multithreaded
if (pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) {
//Check if top left border
if (x < imageData.imageWidth-1) {
if (y < imageData.imageHeight-1) { //Check for top left bar
//Check bottom right pixel
uint8_t *bottomRightPixel = pixel + (imageData.imageWidth + 1)*4;
if (bottomRightPixel[2] == 81 && bottomRightPixel[1] == 162 && bottomRightPixel[0] == 230) {
uint8_t *rightPixel = pixel + (1)*4;
if (rightPixel[0] == 0 && rightPixel[1] == 0 && rightPixel[2] == 0) {
uint8_t *bottomPixel = pixel + (imageData.imageWidth)*4;
if (bottomPixel[0] == 0 && bottomPixel[1] == 0 && bottomPixel[2] == 0) {
struct Position p;p.x=x;p.y=y;
[self addLeftCorner:p top:true];
}
}
}
}
if (y > 0) { //Check for bottom left bar
//Check top right pixel
uint8_t *topRightPixel = pixel + (-imageData.imageWidth + 1)*4;
if (topRightPixel[2] == 40 && topRightPixel[1] == 80 && topRightPixel[0] == 114) {
uint8_t *rightPixel = pixel + (1)*4;
if (rightPixel[0] == 0 && rightPixel[1] == 0 && rightPixel[2] == 0) {
uint8_t *topPixel = pixel - (imageData.imageWidth)*4;
if (topPixel[0] == 0 && topPixel[1] == 0 && topPixel[2] == 0) {
struct Position p;p.x=x;p.y=y;
[self addLeftCorner:p top:false];
}
}
}
}
}
}
}
在 运行 中,addLeftCorner 永远不会被调用,如果我将其注释掉,我将获得额外的 10% CPU 使用率。此外,仅调用方法 processPixel 就占用了 25% CPU 的使用量。
这是为什么?有没有办法优化它?我想拿回那 35% CPU。
TL;DR - 您将需要研究使用 NSObject -methodForSelector: 作为避免 Objective-C 运行时开销的方法。明智地使用它。
-
有一些来自 Smalltalk 的术语对于保持真实情况的心理模型很有用。我们通常指的是 invoking 一个 method 而不是调用一个函数来识别像 Objective-C 这样的动态语言在运行时解析函数地址,每次,响应消息接收者的那种对象。
这种动态调度是动态语言多态性的核心。虽然 Objective-C 运行时几乎达到超人的长度以使方法调用尽可能高效,但它永远不会像直接函数调用那样高效。
大多数时候,方法调用效率低下并不重要。人与应用程序的交互淹没了通常很小的效率损失。但是对于像图像处理这样的计算内核,低效率将变得明显。
通过使用 NSObject -methodForSelector:,您可以将方法解析为函数地址一次,然后跳过运行时查找。仔细研究文档。确保您了解 SEL is and what an IMP 是什么。将方法作为函数调用需要两个额外的参数,self 和 _cmd。确保您了解它们是什么以及如何使用它们。
最重要的是,确保分析绝对证明像这样绕过运行时是必要的。根据您对问题的描述,我不完全相信您看到的正是问题所在。但您是最了解您的软件的人。
你将防止你正在使用这个 method/function 的对象的多态性的可能性,所以它有点具体地设置你设计的这个方面,没有动态语言通常的灵活性.