From 57be198cabd3e18af74b738bfa4e304a2c5b7812 Mon Sep 17 00:00:00 2001 From: CaS Date: Sun, 6 Apr 2003 17:20:04 +0000 Subject: [PATCH] Optimisation of array initialisation and improved docdumentation etc. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@16379 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 5 + Headers/gnustep/base/GSCategories.h | 192 +++++++++++++++++++++++++++- Source/NSArray.m | 74 ++--------- 3 files changed, 207 insertions(+), 64 deletions(-) diff --git a/ChangeLog b/ChangeLog index d733b73cc..c74bef2de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,11 @@ * Source/NSThread.m: (GSPerformHolder) execute perform operations in the same order in which they were submitted. + * Headers/GNUstep/base/GSCategories.h: Added two new macros to + agressively optimise the use of varargs when initialising arrays + etc by avoiding multiple allocation/reallocation of memory to hold + arrays of objects taken from the varargs list. + * Source/NSArray.m: Use the new macros. 2003-04-04 Stephane Corthesy diff --git a/Headers/gnustep/base/GSCategories.h b/Headers/gnustep/base/GSCategories.h index 1c2979913..6b4e3028d 100644 --- a/Headers/gnustep/base/GSCategories.h +++ b/Headers/gnustep/base/GSCategories.h @@ -1,6 +1,6 @@ #ifndef INCLUDED_GS_CATEGORIES_H #define INCLUDED_GS_CATEGORIES_H -/** Declaration of extension methods to standard classes +/** Declaration of extension methods and functions for standard classes Copyright (C) 2003 Free Software Foundation, Inc. @@ -87,5 +87,195 @@ @end #endif + + +#ifndef GS_MAX_OBJECTS_FROM_STACK +/** + * The number of objects to try to get from varargs into an array on + * the stack ... if there are more than this, use the heap. + */ +#define GS_MAX_OBJECTS_FROM_STACK 128 +#endif + +/** + *

This is a macro designed to minimise the use of memory allocation and + * deallocation when you need to work with a vararg list of objects.
+ * The objects are unpacked from the vararg list into two 'C' arrays and + * then a code fragment you specify is able to make use of them before + * that 'C' array is destroyed. + *

+ *

The firstObject argument is the name of the formal parameter in your + * method or function which precedes the ', ...' denoting variable args. + *

+ *

The code argument is a piece of objective-c code to be executed to + * make use of the objects stored in the 'C' arrays.
+ * When this code is called the unsigned integer '__count' will contain the + * number of objects unpacked, the pointer '__objects' will point to + * the first object in each pair, and the pointer '__pairs' will point + * to an array containing the second halves of the pairs of objects + * whose first halves are in '__objects'.
+ * This lets you pack a list of the form 'key, value, key, value, ...' + * into an array of keys and an array of values. + *

+ */ +#define GS_USEIDPAIRLIST(firstObject, code...) ({\ + va_list __ap; \ + unsigned int __max = GS_MAX_OBJECTS_FROM_STACK; \ + unsigned int __count = 0; \ + id __buf[__max/2]; \ + id *__objects = __buf; \ + if (isPaired == YES) \ + { \ + id __buf2[__max/2]; \ + id *__pairs = __buf2; \ + while (__count < __max) \ + { \ + id __tmp = va_arg(__ap, id); \ + if (__tmp == nil) \ + { \ + break; \ + } \ + if ((__count % 2) == 0) \ + { \ + __objects[__count/2] = __tmp; \ + } \ + else \ + { \ + __pairs[__count/2] = __tmp; \ + } \ + if (++__count == __max) \ + { \ + __tmp = va_arg(__ap, id); \ + while (__tmp != nil) \ + { \ + __count++; \ + __tmp = va_arg(__ap, id); \ + } \ + } \ + else if ((__count % 2) == 1) \ + { \ + __count++; \ + __pairs[__count/2] = nil; \ + } \ + } \ + va_end(__ap); \ + if (__count > __max) \ + { \ + unsigned int __tmp; \ + if ((__count % 2) == 1) __count++; \ + __objects = (id*)objc_malloc(__count*sizeof(id)*2); \ + __pairs = &__objects[__count/2]; \ + va_start(__ap, firstObject); \ + for (__tmp = 0; __tmp < __count; __tmp++) \ + { \ + if ((__count % 2) == 0) \ + { \ + __objects[__count/2] = va_arg(__ap, id); \ + } \ + else \ + { \ + __pairs[__count/2] = va_arg(__ap, id); \ + } \ + } \ + va_end(__ap); \ + } \ + code; \ + if (__objects != __buf) objc_free(__objects); \ + } \ + else \ + { \ + va_start(__ap, firstObject); \ + while (__count < __max) \ + { \ + __objects[__count] = va_arg(__ap, id); \ + if (__objects[__count] == nil) \ + { \ + break; \ + } \ + if (++__count == __max) \ + { \ + id __tmp = va_arg(__ap, id); \ + while (__tmp != nil) \ + { \ + __count++; \ + __tmp = va_arg(__ap, id); \ + } \ + } \ + } \ + va_end(__ap); \ + if (__count > __max) \ + { \ + unsigned int __tmp; \ + __objects = (id*)objc_malloc(__count*sizeof(id)); \ + va_start(__ap, firstObject); \ + for (__tmp = 0; __tmp < __count; __tmp++) \ + { \ + __objects[__tmp] = va_arg(__ap, id); \ + } \ + va_end(__ap); \ + } \ + code; \ + if (__objects != __buf) objc_free(__objects); \ + } \ +}) + +/** + *

This is a macro designed to minimise the use of memory allocation and + * deallocation when you need to work with a vararg list of objects.
+ * The objects are unpacked from the vararg list into a 'C' array and + * then a code fragment you specify is able to make use of them before + * that 'C' array is destroyed. + *

+ *

The firstObject argument is the name of the formal parameter in your + * method or function which precedes the ', ...' denoting variable args. + *

+ *

The code argument is a piece of objective-c code to be executed to + * make use of the objects stored in the 'C' array.
+ * When this code is called the unsigned integer '__count' will contain the + * number of objects unpacked, and the pointer '__objects' will point to + * the unpacked objects. + *

+ */ +#define GS_USEIDLIST(firstObject, code...) ({\ + va_list __ap; \ + unsigned int __max = GS_MAX_OBJECTS_FROM_STACK; \ + unsigned int __count = 0; \ + id __buf[__max]; \ + id *__objects = __buf; \ + va_start(__ap, firstObject); \ + while (__count < __max) \ + { \ + __objects[__count] = va_arg(__ap, id); \ + if (__objects[__count] == nil) \ + { \ + break; \ + } \ + if (++__count == __max) \ + { \ + id __tmp = va_arg(__ap, id); \ + while (__tmp != nil) \ + { \ + __count++; \ + __tmp = va_arg(__ap, id); \ + } \ + } \ + } \ + va_end(__ap); \ + if (__count > __max) \ + { \ + unsigned int __tmp; \ + __objects = (id*)objc_malloc(__count*sizeof(id)); \ + va_start(__ap, firstObject); \ + for (__tmp = 0; __tmp < __count; __tmp++) \ + { \ + __objects[__tmp] = va_arg(__ap, id); \ + } \ + va_end(__ap); \ + } \ + code; \ + if (__objects != __buf) objc_free(__objects); \ +}) + + #endif /* NO_GNUSTEP */ #endif /* INCLUDED_GS_CATEGORIES_H */ diff --git a/Source/NSArray.m b/Source/NSArray.m index a5f7684dc..7be56a642 100644 --- a/Source/NSArray.m +++ b/Source/NSArray.m @@ -43,6 +43,7 @@ #include #include #include +#include "gnustep/base/GSCategories.h" #include "GSPrivate.h" extern BOOL GSMacOSXCompatiblePropertyLists(void); @@ -67,13 +68,10 @@ static GSPlaceholderArray *defaultPlaceholderArray; static NSMapTable *placeholderMap; static NSLock *placeholderLock; -@interface NSArray (GSPrivate) -- (id) _initWithObjects: firstObject rest: (va_list) ap; -@end - - /** - * A simple, low overhead, ordered container for objects. + * A simple, low overhead, ordered container for objects. All the objects + * in the container are retained by it. The container may not contain nil + * (though it may contain [NSNull+null]). */ @implementation NSArray @@ -222,12 +220,11 @@ static SEL rlSel; */ + (id) arrayWithObjects: firstObject, ... { - va_list ap; - va_start(ap, firstObject); - self = [[self allocWithZone: NSDefaultMallocZone()] - _initWithObjects: firstObject rest: ap]; - va_end(ap); - return AUTORELEASE(self); + id a = [self allocWithZone: NSDefaultMallocZone()]; + + GS_USEIDLIST(firstObject, + a = [a initWithObjects: __objects count: __count]); + return AUTORELEASE(a); } /** @@ -611,63 +608,14 @@ static SEL rlSel; return nil; } -- (id) _initWithObjects: firstObject rest: (va_list) ap -{ - register unsigned i; - register unsigned curSize; - auto unsigned prevSize; - auto unsigned newSize; - auto id *objsArray; - auto id tmpId; - - /* Do initial allocation. */ - prevSize = 3; - curSize = 5; - objsArray = (id*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(id) * curSize); - tmpId = firstObject; - - /* Loop through adding objects to array until a nil is - * found. - */ - for (i = 0; tmpId != nil; i++) - { - /* Put id into array. */ - objsArray[i] = tmpId; - - /* If the index equals the current size, increase size. */ - if (i == curSize - 1) - { - /* Fibonacci series. Supposedly, for this application, - * the fibonacci series will be more memory efficient. - */ - newSize = prevSize + curSize; - prevSize = curSize; - curSize = newSize; - - /* Reallocate object array. */ - objsArray = (id*)NSZoneRealloc(NSDefaultMallocZone(), objsArray, - sizeof(id) * curSize); - } - tmpId = va_arg(ap, id); - } - va_end( ap ); - - /* Put object ids into NSArray. */ - self = [self initWithObjects: objsArray count: i]; - NSZoneFree(NSDefaultMallocZone(), objsArray); - return( self ); -} - /** * Initialize the array the list of objects. *
May change the value of self before returning it. */ - (id) initWithObjects: firstObject, ... { - va_list ap; - va_start(ap, firstObject); - self = [self _initWithObjects: firstObject rest: ap]; - va_end(ap); + GS_USEIDLIST(firstObject, + self = [self initWithObjects: __objects count: __count]); return self; }