开发者

reimplementing objc_msgSend() and its siblings in objc2.0 on iPhone

开发者 https://www.devze.com 2023-03-09 15:46 出处:网络
I want to do something like this: #import <sys/time.h> typedef long long int64; int64 int64Micro(void)

I want to do something like this:

#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;
}

to be able to log for every method at a central place in code, when it is getting very expensive (the 1.000µs or 1 ms is just an example-value, can be adjusted of course, but should not be to small, as otherwise the logging will cost more time as the execution of the method, being logged, for most methods).

As on iPhone dynamic libs aren't possible, there is no way, to replace the objc_msgSend-implementatio开发者_JS百科n, which is called by precompiled frameworks, without recompiling them, but in my code actually my implementation is called instead of the one from the objc-runtime.

But this line

id retVal = 0;//objc_msgSendv(self, op, method_getSizeOfArguments(op), argptr)

is commented out, as it should work in objc 1.0, but in objc 2.0 objc_msgSendv() isn't available anymore, same as method_getSizeOfArguments().

So, is there any way to do this, without having to rebuild the objc runtime and without having to re-implement the original behavior of objc_msgSend and it's siblings by copy-and paste those thousands of platform dependent assembly code lines from the source of the objc runtime?

I already thought about GCC's __builtin_apply() function, to call the original objc_msgSend(), but it seems, there is no way to know the size in bytes of the variable parameters passed to objc_msgSend() for a certain call to it.


Swapping out objc_msgSend to measure performance like this is a bad idea for a few reasons.

  1. There are actually a number of objc_msgSend variants depending on the parameter types and return types. You'd have to re-implement all of those.
  2. The performance of your app is likely to degrade massively. When you're trying to measure performance, this isn't the best way to do it.
  3. For certain selectors, the compiler actually issues a objc_msgSend_fixup function pointer, which is then dynamically optimized into a vtable lookup. This avoids the traditional objc_msgSend entirely. You'd have to catch all of those too.
  4. objc_msgSend can not be implemented in C, as it is not actually variadic function and does not have a prototype that can be represented in C. From the caller's perspective, objc_msgSend is an opaque trampoline; it will dispatch to the method IMP, which is itself expected to implement the function ABI required by the caller.

There are much better ways to measure performance.

If Instruments isn't showing your slow areas, you may be profiling only running threads instead of all threads. Using the Time Profiler instrument, click the little 'i' next to the track header, and then select "All Thread States". This will make sure that time spent waiting on I/O is counted in your measurements, which by default isn't counted.

reimplementing objc_msgSend() and its siblings in objc2.0 on iPhone

If for some reason that still isn't working and you just want to record a hard list of all selector running times, you could add a DTrace probe using the objc DTrace provider to record the running time of every Objective-C method. This wouldn't record statistics for C functions like Instruments will, and it won't organize things hierarchically, but if Instruments isn't doing it for you, this will. In Instruments, choose Instrument -> Trace Symbol. In the window that appears, type the following:

-[* *]

That is, trace all instance methods (-) called on any class (*) of any selector name (*). Then record, and you'll get a list of the time consumed in every method. Further, this does not defeat the hand-tuned assembler in the built-in objc_msgSend, and is fully supported by Apple. It is also not limited to only your code; it will profile calls inside Apple's frameworks as well.

Unfortunately, DTrace probes are not currently supported on iOS devices, so you'd have to use the DTrace probe against an app running in the simulator. This is obviously not ideal. If you've really got a single method serving as a hot spot, though, it's likely that it will show up in the simulator as well.

Again, though, the Time Profile instrument is far better suited to the job, and it definitely works. Turn on All Thread States, and you'll see your problem.

0

精彩评论

暂无评论...
验证码 换一张
取 消