在iPhone上的objc2.0中重新实现objc_msgSend()及其兄弟姐妹

Kai*_*udi 1 iphone gcc runtime objective-c

我想做这样的事情:

#import <sys/time.h>

typedef long long int64;

int64 int64Micro(void)
{
    struct timeval timestruct;
    gettimeofday(&timestruct, NULL);
    return ((int64)timestruct.tv_sec)*((int64)1000000)+(int64)timestruct.tv_usec;
}

int64 lasttime = 0;

id objc_msgSend(id self, SEL op, ...)
{
    int64 starttime = int64Micro();
    va_list argptr;
    va_start(argptr, op);
    id retVal = 0;//objc_msgSendv(self, op, method_getSizeOfArguments(op), argptr);
    va_end(argptr);
    int64 cost = int64Micro()-starttime;
    if(cost > 1000)
        NSLog(@"%@() : %lld µs\n", NSStringFromSelector(op), cost);
    return retVal;
}
Run Code Online (Sandbox Code Playgroud)

为了能够在代码的中心位置记录每个方法,当它变得非常昂贵时(1.000μs或1 ms只是一个示例值,当然可以调整,但不应该很小,否则对于大多数方法,记录将花费更多时间作为记录方法的执行.

因为iPhone上的动态库是不可能的,所以无法替换由预编译框架调用的objc_msgSend实现,而无需重新编译它们,但在我的代码实际上我的实现被调用而不是来自objc-运行.

但这一行

id retVal = 0;//objc_msgSendv(self, op, method_getSizeOfArguments(op), argptr)
Run Code Online (Sandbox Code Playgroud)

被注释掉,因为它应该在objc 1.0中工作,但在objc 2.0中objc_msgSendv()不再可用,与method_getSizeOfArguments()相同.

那么,有没有办法做到这一点,无需重建objc运行时,无需重新实现objc_msgSend的原始行为,并且它的兄弟姐妹通过复制并粘贴来自数据库的数千个平台相关的汇编代码行. objc运行时?

我已经考虑过GCC的__builtin_apply()函数来调用原始的objc_msgSend(),但似乎没有办法知道传递给objc_msgSend()的变量参数的大小(以字节为单位)来调用它.

BJ *_*mer 10

objc_msgSend由于一些原因,交换出来测量这样的性能是一个坏主意.

  1. 实际上有许多objc_msgSend变体,具体取决于参数类型和返回类型.你必须重新实施所有这些.
  2. 您的应用的性能可能会大幅降低.当您尝试衡量性能时,这不是最好的方法.
  3. 对于某些选择器,编译器实际发出一个objc_msgSend_fixup函数指针,然后将其动态优化为vtable查找.这完全避免了传统的objc_msgSend.你也必须抓住所有这些.
  4. objc_msgSend不能在C中实现,因为它实际上不是可变函数,并且没有可用C表示的原型.从调用者的角度来看,objc_msgSend是一个不透明的蹦床; 它将调度到IMP方法,IMP本身应该实现调用者所需的函数ABI.

有更好的方法来衡量绩效.

如果Instruments没有显示您的慢速区域,您可能只分析正在运行的线程而不是所有线程.使用Time Profiler工具,单击轨道标题旁边的小"i",然后选择"所有线程状态".这将确保在您的测量中计算等待I/O的时间,默认情况下不会计算.

分析所有线程状态

如果由于某种原因仍然无法工作并且您只想记录所有选择器运行时间的硬列表,则可以使用objc DTrace提供程序添加DTrace探测器以记录每个Objective-C方法的运行时间.这不会记录像Instruments这样的C函数的统计信息,也不会按层次组织事物,但是如果Instruments没有为你做这些事情,那么就会这样做.在仪器中,选择Instrument- > Trace Symbol.在出现的窗口中,键入以下内容:

-[* *]
Run Code Online (Sandbox Code Playgroud)

也就是说,跟踪-*任何选择器名称(*)的任何class()上调用的所有实例方法().然后记录,你会得到每种方法消耗的时间列表.此外,这并没有打败内置的手动调整汇编程序objc_msgSend,并得到Apple的全力支持.它也不仅限于您的代码; 它还将描述Apple框架内的调用.

遗憾的是,iOS设备目前不支持DTrace探针,因此您必须针对在模拟器中运行的应用程序使用DTrace探针.这显然不太理想.但是,如果你真的有一种方法可以作为热点,那么它很可能会出现在模拟器中.

尽管如此,Time Profile仪器更适合这项工作,它绝对有效.打开所有线程状态,您将看到您的问题.