This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[RFC] Optimizing objective-c message dispatch
- From: Alexander Malmberg <alexander at malmberg dot org>
- To: gcc-patches at gcc dot gnu dot org
- Date: Wed, 11 Feb 2004 03:44:18 +0100
- Subject: [RFC] Optimizing objective-c message dispatch
In an effort to get objective-c code to run faster, I've tinkered with
the main message dispatch function, objc_msg_lookup. gcc generates a
call to this function for each message send expression, so in any
reasonably messaging-heavy objective-c code, there'll be tons of calls
to it.
The attached patch makes the message lookup faster by having gcc use a
new function for message dispatching, objc_msg_lookup_fast, and by
marking giving it a 'regparm(2)' attribute. libobjc/sendmsg_opt.S
(attached) contains an optimized implementation of this function in ix86
assembly. Additionally, some runtime structures are tweaked a bit to
enable optimizations in objc_msg_lookup_fast.
objc_msg_lookup_fast only handles the common case where the dispatch
tables are already installed, and the receiver isn't nil. In other cases
(should be rare), it falls back on objc_msg_lookup. objc_msg_lookup
remains unchanged since objc_msg_lookup_fast uses it, and since this
maintains compatibility with old compiled code.
For pure message lookups, this is much much faster. To measure the
impact on normal code, I compared code sizes before and after for some
semi-randomly chosen objective-c code (the basic GNUstep libraries, and
a GNUstep application):
before after
libgnustep-base.so 2410464 2329280
libgnustep-gui.so 2970076 2855068
libgnustep-art 490872 483192
Terminal 208516 198596
In total, a 3.5% decrease in code size. To measure performance, I used a
hacked up version of a simple benchmark for some common parts of
gnustep-base. Running it three times before and after gave (time in
seconds):
before: 174.51 174.60 174.55
after: 164.35 164.18 163.91
In total, a 6.0% decrease in running time.
Obviously, this patch is highly unportable (regparm and ix86 assembly).
Would it be worthwhile for me to try to make it portable so that it
could be applied? If so, how should I do that?
- Alexander Malmberg
.globl objc_msg_lookup_fast
.type objc_msg_lookup_fast,@function
.align 16
objc_msg_lookup_fast:
testl %eax,%eax
jz .Lwont_handle1
pushl %eax
movl (%eax),%eax
movl 32(%eax),%eax
movl (%edx),%ecx
cmpl 24(%eax),%ecx
jae .Lwont_handle2
movl (%eax),%eax
shrl $16,%ecx
movl (%eax,%ecx,4),%eax
movl (%edx),%ecx
andl $0xffff,%ecx
movl (%eax,%ecx,4),%eax
testl %eax,%eax
jz .Lwont_handle2
addl $4,%esp
ret
.Lwont_handle1:
pushl %edx
pushl %eax
call objc_msg_lookup
addl $8,%esp
ret
.Lwont_handle2:
popl %eax
pushl %edx
pushl %eax
call objc_msg_lookup
addl $8,%esp
ret
? libobjc/sendmsg_opt.S
Index: gcc/objc/objc-act.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/objc/objc-act.c,v
retrieving revision 1.203
diff -u -r1.203 objc-act.c
--- gcc/objc/objc-act.c 18 Jan 2004 01:15:28 -0000 1.203
+++ gcc/objc/objc-act.c 11 Feb 2004 00:39:01 -0000
@@ -519,7 +540,7 @@
{
TAG_GETCLASS = "objc_get_class";
TAG_GETMETACLASS = "objc_get_meta_class";
- TAG_MSGSEND = "objc_msg_lookup";
+ TAG_MSGSEND = "objc_msg_lookup_fast";
TAG_MSGSENDSUPER = "objc_msg_lookup_super";
/* GNU runtime does not provide special functions to support
structure-returning methods. */
@@ -1171,31 +1159,37 @@
get_identifier (TAG_SUPER)));
- /* id objc_msgSend (id, SEL, ...); */
-
- temp_type
- = build_function_type (id_type,
- tree_cons (NULL_TREE, id_type,
- tree_cons (NULL_TREE, selector_type,
- NULL_TREE)));
-
if (! flag_next_runtime)
{
- umsg_decl = build_decl (FUNCTION_DECL,
- get_identifier (TAG_MSGSEND), temp_type);
- DECL_EXTERNAL (umsg_decl) = 1;
- TREE_PUBLIC (umsg_decl) = 1;
- DECL_INLINE (umsg_decl) = 1;
- DECL_ARTIFICIAL (umsg_decl) = 1;
+ /* id objc_msg_lookup (id, SEL); */
+ temp_type
+ = build_function_type (id_type,
+ tree_cons (NULL_TREE, id_type,
+ tree_cons (NULL_TREE, selector_type,
+ OBJC_VOID_AT_END)));
+
+ umsg_decl = builtin_function (TAG_MSGSEND,
+ temp_type, 0, NOT_BUILT_IN,
+ NULL, NULL_TREE);
- make_decl_rtl (umsg_decl, NULL);
- pushdecl (umsg_decl);
+ decl_attributes (&umsg_decl,
+ tree_cons (get_identifier ("regparm"),
+ tree_cons (NULL_TREE,
+ build_int_2 (2, 0), NULL_TREE), NULL_TREE), 0);
}
else
{
+ temp_type
+ = build_function_type (id_type,
+ tree_cons (NULL_TREE, id_type,
+ tree_cons (NULL_TREE, selector_type,
+ NULL_TREE)));
+
+ /* id objc_msgSend (id, SEL, ...); */
umsg_decl = builtin_function (TAG_MSGSEND,
temp_type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
+
/* id objc_msgSendNonNil (id, SEL, ...); */
umsg_nonnil_decl = builtin_function (TAG_MSGSEND_NONNIL,
temp_type, 0, NOT_BUILT_IN,
Index: libobjc/Makefile.in
===================================================================
RCS file: /cvsroot/gcc/gcc/libobjc/Makefile.in,v
retrieving revision 1.28.10.3
diff -u -r1.28.10.3 Makefile.in
--- libobjc/Makefile.in 9 Feb 2004 22:27:17 -0000 1.28.10.3
+++ libobjc/Makefile.in 11 Feb 2004 00:39:19 -0000
@@ -143,13 +143,13 @@
OBJS = archive.lo class.lo encoding.lo gc.lo hash.lo init.lo linking.lo \
misc.lo nil_method.lo NXConstStr.lo Object.lo objects.lo \
Protocol.lo sarray.lo selector.lo sendmsg.lo thr.lo \
- $(OBJC_THREAD_FILE).lo
+ $(OBJC_THREAD_FILE).lo sendmsg_opt.lo
OBJS_GC = archive_gc.lo class_gc.lo encoding_gc.lo gc_gc.lo hash_gc.lo \
init_gc.lo linking_gc.lo misc_gc.lo nil_method_gc.lo \
NXConstStr_gc.lo Object_gc.lo objects_gc.lo Protocol_gc.lo \
sarray_gc.lo selector_gc.lo sendmsg_gc.lo thr_gc.lo \
- $(OBJC_THREAD_FILE)_gc.lo
+ $(OBJC_THREAD_FILE)_gc.lo sendmsg_opt.lo
runtime-info.h:
echo "" > tmp-runtime.m
@@ -242,6 +242,9 @@
sendmsg_gc.lo: sendmsg.c runtime-info.h
$(LIBTOOL_COMPILE) $(CC) -c -o $@ $(ALL_CFLAGS) $(OBJC_GCFLAGS) \
$(INCLUDES) $<
+
+sendmsg_opt.lo: sendmsg_opt.S
+ $(LIBTOOL_COMPILE) $(CC) -c -o $@ $(ALL_CFLAGS) $(INCLUDES) $<
thr_gc.lo: thr.c
$(LIBTOOL_COMPILE) $(CC) -c -o $@ $(ALL_CFLAGS) $(OBJC_GCFLAGS) \
Index: libobjc/sarray.c
===================================================================
RCS file: /cvsroot/gcc/gcc/libobjc/sarray.c,v
retrieving revision 1.8.6.1
diff -u -r1.8.6.1 sarray.c
--- libobjc/sarray.c 28 Jan 2004 08:00:00 -0000 1.8.6.1
+++ libobjc/sarray.c 11 Feb 2004 00:39:25 -0000
@@ -222,6 +222,7 @@
/* Initialize members */
#ifdef OBJC_SPARSE3
arr->capacity = num_indices*INDEX_CAPACITY;
+ arr->pcapacity = soffset_encode(arr->capacity);
new_indices = (struct sindex **)
objc_malloc (sizeof (struct sindex *) * num_indices);
@@ -234,6 +235,7 @@
#else /* OBJC_SPARSE2 */
arr->capacity = num_indices*BUCKET_SIZE;
+ arr->pcapacity = soffset_encode(arr->capacity);
new_buckets = (struct sbucket **)
objc_malloc (sizeof (struct sbucket *) * num_indices);
@@ -328,6 +330,7 @@
/* update capacity */
array->capacity = rounded_size;
+ array->pcapacity = soffset_encode(array->capacity);
#ifdef OBJC_SPARSE3
/* alloc to force re-read by any concurrent readers. */
@@ -496,6 +499,7 @@
oarr->ref_count += 1;
arr->is_copy_of = oarr;
arr->capacity = oarr->capacity;
+ arr->pcapacity = soffset_encode(arr->capacity);
#ifdef OBJC_SPARSE3
/* Copy bucket table */
Index: libobjc/objc/sarray.h
===================================================================
RCS file: /cvsroot/gcc/gcc/libobjc/objc/sarray.h,v
retrieving revision 1.4
diff -u -r1.4 sarray.h
--- libobjc/objc/sarray.h 23 May 2003 20:04:58 -0000 1.4
+++ libobjc/objc/sarray.h 11 Feb 2004 00:39:26 -0000
@@ -96,8 +96,8 @@
unsigned int eoffset : BUCKET_BITS;
unsigned int unused : 2;
#else
- unsigned int boffset : SIZET_BITS/2;
unsigned int eoffset : SIZET_BITS/2;
+ unsigned int boffset : SIZET_BITS/2;
#endif
#endif /* OBJC_SPARSE2 */
};
@@ -140,6 +140,7 @@
short ref_count;
struct sarray* is_copy_of;
size_t capacity;
+ size_t pcapacity;
};
struct sarray* sarray_new(int, void* default_element);