This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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);

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]