diff --git a/Headers/gnustep/base/fast.x b/Headers/gnustep/base/fast.x new file mode 100644 index 000000000..99a4b07ae --- /dev/null +++ b/Headers/gnustep/base/fast.x @@ -0,0 +1,151 @@ +/* Performance enhancing utilities GNUStep + Copyright (C) 1998 Free Software Foundation, Inc. + + Written by: Richard Frith-Macdonald + Date: October 1998 + + This file is part of the GNUstep Base Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#ifndef INLINE +#define INLINE inline +#endif + +/* + * This file is all to do with improving performance by avoiding the + * Objective-C messaging overhead in time-critical code. + * + * The motiviation behind it is to keep all the information needed to + * do that in one place (using a single mechanism), so that optimization + * attempts can be kept track of. + * + * The optimisations are of three sorts - + * + * 1. inline functions + * There are many operations that can be speeded up by using inline + * code to examine objects rather than sending messages to them to + * ask them about themselves. Often, the objc runtime provides + * functions to do this, but these sometimes perform unnecessary + * checks. Here we attempt to provide some basics. + * + * 2. class comparison + * It is often necessary to check the class of objects - instead of + * using [+class] method, we can cache certain classes in a global + * structure (The [NSObject +initialize] method does the caching) + * and refer to the structure elements directly. + * + * 3. direct method despatch + * A common techique is to obtain the method implementation for a + * specific message sent to a particular class of object, and call + * the implementation directly to avoid repeated lookup within the + * objc runtime. + * While there is no huge speed advantage to caching the method + * implementations, it does make it easy to search the source for + * code that is using this technique and referring to a cached + * method implementation. + */ + +/* + * Structure to cache class information. + * By convention, the name of the structure element is the name of the + * class with an underscore prepended. + */ +typedef struct { + /* + * String classes + */ + Class _NSString; + Class _NSGString; + Class _NSGMutableString; + Class _NSGCString; + Class _NSGMutableCString; + Class _NXConstantString; +} fastCls; +extern fastCls _fastCls; /* Populated by NSObject */ + +/* + * Structure to cache method implementation information. + * By convention, the name of the structure element consists of an + * underscore followed by the name of the class, another underscore, and + * the name of the method (with colons replaced by underscores). + */ +typedef struct { + /* + * String implementations. + */ + unsigned (*_NSString_hash)(); + unsigned (*_NSGString_hash)(); + BOOL (*_NSGString_isEqual_)(); + BOOL (*_NSGCString_isEqual_)(); +} fastImp; +extern fastImp _fastImp; /* Populated by NSObject */ + +/* + * Fast access to class info - DON'T pass nil to these! + */ + +static INLINE BOOL +fastIsInstance(id obj) +{ + return CLS_ISCLASS(obj->class_pointer); +} + +static INLINE Class +fastClass(NSObject* obj) +{ + return ((id)obj)->class_pointer; +} + +static INLINE Class +fastClassOfInstance(NSObject* obj) +{ + if (fastIsInstance((id)obj)) + return fastClass(obj); + return Nil; +} + +static INLINE Class +fastSuper(Class cls) +{ + return cls->super_class; +} + +static INLINE BOOL +fastClassIsKindOfClass(Class c0, Class c1) +{ + while (c0 != Nil) { + if (c0 == c1) + return YES; + c0 = class_get_super_class(c0); + } + return NO; +} + +static INLINE BOOL +fastInstanceIsKindOfClass(NSObject *obj, Class c) +{ + Class ic = fastClassOfInstance(obj); + + if (ic == Nil) + return NO; + return fastClassIsKindOfClass(ic, c); +} + diff --git a/Source/FastMap.x b/Source/FastMap.x index c7d10e9d0..bdab08fdd 100644 --- a/Source/FastMap.x +++ b/Source/FastMap.x @@ -26,6 +26,11 @@ #include #include +/* To easily un-inline functions for debugging */ +#ifndef INLINE +#define INLINE inline +#endif + /* * This file should be INCLUDED in files wanting to use the FastMap * functions - these are all declared inline for maximum performance. @@ -86,9 +91,6 @@ #define FAST_MAP_EQUAL(X,Y) [(X).o isEqual: (Y).o] #endif -/* To easily un-inline functions for debugging */ -#define INLINE inline - typedef union { NSObject *o; long int i; @@ -125,6 +127,9 @@ struct _FastMapTable { FastMapNode firstNode; /* List for enumerating. */ size_t bucketCount; /* Number of buckets in map. */ FastMapBucket buckets; /* Array of buckets. */ + FastMapNode freeNodes; /* List of unused nodes. */ + size_t chunkCount; /* Number of chunks in array. */ + FastMapNode *nodeChunks; /* Chunks of allocated memory. */ }; struct _FastMapEnumerator { @@ -245,49 +250,96 @@ FastMapRemangleBuckets(FastMapTable map, } } +static INLINE void +FastMapMoreNodes(FastMapTable map) +{ + FastMapNode *newArray; + size_t arraySize = (map->chunkCount+1)*sizeof(FastMapNode); + + newArray = (FastMapNode*)NSZoneMalloc(map->zone, arraySize); + + if (newArray) { + FastMapNode newNodes; + size_t chunkCount; + size_t chunkSize; + + memcpy(newArray,map->nodeChunks,(map->chunkCount)*sizeof(FastMapNode)); + if (map->nodeChunks != 0) + NSZoneFree(map->zone, map->nodeChunks); + map->nodeChunks = newArray; + + if (map->chunkCount == 0) { + chunkCount = map->bucketCount > 1 ? map->bucketCount : 2; + } + else { + chunkCount = ((map->nodeCount>>2)+1)<<1; + } + chunkSize = chunkCount * sizeof(FastMapNode_t); + newNodes = (FastMapNode)NSZoneMalloc(map->zone, chunkSize); + if (newNodes) { + map->nodeChunks[map->chunkCount++] = newNodes; + newNodes[--chunkCount].nextInMap = map->freeNodes; + while (chunkCount--) { + newNodes[chunkCount].nextInMap = &newNodes[chunkCount+1]; + } + map->freeNodes = newNodes; + } + } +} + #if FAST_MAP_HAS_VALUE static INLINE FastMapNode FastMapNewNode(FastMapTable map, FastMapItem key, FastMapItem value) { - FastMapNode node; + FastMapNode node = map->freeNodes; - node = (FastMapNode)NSZoneMalloc(map->zone, sizeof(FastMapNode_t)); - - if (node != 0) { - node->key = key; - node->value = value; - node->nextInBucket = 0; - node->nextInMap = 0; + if (node == 0) { + FastMapMoreNodes(map); + node = map->freeNodes; + if (node == 0) { + return 0; + } } + + map->freeNodes = node->nextInMap; + node->key = key; + node->value = value; + node->nextInBucket = 0; + node->nextInMap = 0; + return node; } #else static INLINE FastMapNode FastMapNewNode(FastMapTable map, FastMapItem key) { - FastMapNode node; + FastMapNode node = map->freeNodes; - node = (FastMapNode)NSZoneMalloc(map->zone, sizeof(FastMapNode_t)); - - if (node != 0) { - node->key = key; - node->nextInBucket = 0; - node->nextInMap = 0; + if (node = 0) { + FastMapMoreNodes(map); + node = map->freeNodes; + if (node == 0) { + return 0; + } } + + map->freeNodes = node->nextInMap; + node->key = key; + node->nextInBucket = 0; + node->nextInMap = 0; return node; } #endif static INLINE void -FastMapFreeNode(FastMapNode node) +FastMapFreeNode(FastMapTable map, FastMapNode node) { - if (node != 0) { - FAST_MAP_RELEASE_KEY(node->key); + FAST_MAP_RELEASE_KEY(node->key); #if FAST_MAP_HAS_VALUE - FAST_MAP_RELEASE_VAL(node->value); + FAST_MAP_RELEASE_VAL(node->value); #endif - NSZoneFree(NSZoneFromPointer(node), node); - } + node->nextInMap = map->freeNodes; + map->freeNodes = node; } static INLINE FastMapNode @@ -312,7 +364,7 @@ FastMapNodeForKey(FastMapTable map, FastMapItem key) return node; } -static INLINE size_t +static INLINE void FastMapResize(FastMapTable map, size_t new_capacity) { FastMapBucket new_buckets; @@ -352,12 +404,9 @@ FastMapResize(FastMapTable map, size_t new_capacity) map->buckets = new_buckets; map->bucketCount = size; } - - /* Return the new capacity. */ - return map->bucketCount; } -static INLINE size_t +static INLINE void FastMapRightSizeMap(FastMapTable map, size_t capacity) { /* FIXME: Now, this is a guess, based solely on my intuition. If anyone @@ -366,10 +415,7 @@ FastMapRightSizeMap(FastMapTable map, size_t capacity) * L. Jones . */ if (3 * capacity >= 4 * map->bucketCount) { - return FastMapResize(map, (3 * capacity)/4 + 1); - } - else { - return map->bucketCount; + FastMapResize(map, (3 * capacity)/4 + 1); } } @@ -497,7 +543,7 @@ FastMapRemoveKey(FastMapTable map, FastMapItem key) if (node != 0) { FastMapRemoveNodeFromMap(map, bucket, node); - FastMapFreeNode(node); + FastMapFreeNode(map, node); } } } @@ -513,17 +559,28 @@ FastMapEmptyMap(FastMapTable map) FastMapNode node = bucket->firstNode; FastMapRemoveNodeFromBucket(bucket, node); - FastMapFreeNode(node); + FastMapFreeNode(map, node); } bucket++; } if (map->buckets != 0) { NSZoneFree(map->zone, map->buckets); + map->buckets = 0; + map->bucketCount = 0; } + if (map->nodeChunks != 0) { + for (i = 0; i < map->chunkCount; i++) { + NSZoneFree(map->zone, map->nodeChunks[i]); + } + map->chunkCount = 0; + NSZoneFree(map->zone, map->nodeChunks); + map->nodeChunks = 0; + } + map->firstNode = 0; map->nodeCount = 0; - map->buckets = 0; - map->bucketCount = 0; + map->freeNodes = 0; + map->zone = 0; } static INLINE FastMapTable @@ -534,7 +591,11 @@ FastMapInitWithZoneAndCapacity(FastMapTable map, NSZone *zone, size_t capacity) map->bucketCount = 0; map->firstNode = 0; map->buckets = 0; + map->nodeChunks = 0; + map->freeNodes = 0; + map->chunkCount = 0; FastMapRightSizeMap(map, capacity); + FastMapMoreNodes(map); } diff --git a/Source/NSGCString.m b/Source/NSGCString.m index 685ae2f5b..16d733ea0 100644 --- a/Source/NSGCString.m +++ b/Source/NSGCString.m @@ -33,25 +33,10 @@ #include #include - -static Class immutableClass; -static Class mutableClass; -static Class constantClass; +#include @implementation NSGCString -+ (void) initialize -{ - static int done = 0; - if (!done) - { - done = 1; - immutableClass = [NSGCString class]; - mutableClass = [NSGMutableCString class]; - constantClass = [NXConstantString class]; - } -} - - (void)dealloc { if (_free_contents) @@ -62,7 +47,7 @@ static Class constantClass; - (unsigned) hash { if (_hash == 0) - if ((_hash = [super hash]) == 0) + if ((_hash = _fastImp._NSString_hash(self, @selector(hash))) == 0) _hash = 0xffffffff; return _hash; } @@ -274,18 +259,19 @@ static Class constantClass; Class c; if (anObject == self) return YES; - c = [anObject class]; + c = fastClassOfInstance(anObject); - if (c == immutableClass || c == mutableClass || c == constantClass) + if (c == _fastCls._NSGCString || c == _fastCls._NSGMutableCString || c == _fastCls._NXConstantString) { NSGCString *other = (NSGCString*)anObject; if (_count != other->_count) return NO; if (_hash == 0) - if ((_hash = [super hash]) == 0) + if ((_hash = _fastImp._NSString_hash(self, @selector(hash))) == 0) _hash = 0xffffffff; - if (other->_hash == 0) [other hash]; + if (other->_hash == 0) + _fastImp._NSGString_hash(other, @selector(hash)); if (_hash != other->_hash) return NO; if (memcmp(_contents_chars, other->_contents_chars, _count) != 0) @@ -302,15 +288,18 @@ static Class constantClass; { Class c; - c = [aString class]; - if (c == immutableClass || c == mutableClass || c == constantClass) + c = fastClassOfInstance(aString); + if (c == _fastCls._NSGCString || c == _fastCls._NSGMutableCString || c == _fastCls._NXConstantString) { NSGCString *other = (NSGCString*)aString; if (_count != other->_count) return NO; - if (_hash == 0) [self hash]; - if (other->_hash == 0) [other hash]; + if (_hash == 0) + if ((_hash = _fastImp._NSString_hash(self, @selector(hash))) == 0) + _hash = 0xffffffff; + if (other->_hash == 0) + _fastImp._NSGString_hash(other, @selector(hash)); if (_hash != other->_hash) return NO; if (memcmp(_contents_chars, other->_contents_chars, _count) != 0) diff --git a/Source/NSGDictionary.m b/Source/NSGDictionary.m index f611386f6..f7aaa808b 100644 --- a/Source/NSGDictionary.m +++ b/Source/NSGDictionary.m @@ -28,10 +28,72 @@ #include #include +#include + +/* + * Evil hack - this structure MUST correspond to the layout of all + * instances of the string classes we know about! + */ +typedef struct { + Class *isa; + char *_contents_chars; + int _count; + BOOL _free_when_done; + unsigned _hash; +} *dictAccessToStringHack; + +static INLINE unsigned +myHash(NSObject *obj) +{ + if (fastIsInstance(obj)) { + Class c = fastClass(obj); + + if (c == _fastCls._NXConstantString || + c == _fastCls._NSGCString || + c == _fastCls._NSGMutableCString || + c == _fastCls._NSGString || + c == _fastCls._NSGMutableString) { + + if (((dictAccessToStringHack)obj)->_hash != 0) { + return ((dictAccessToStringHack)obj)->_hash; + } + return _fastImp._NSGString_hash(obj, @selector(hash)); + } + } + return [obj hash]; +} + +static INLINE BOOL +myEqual(NSObject *self, NSObject *other) +{ + if (self == other) { + return YES; + } + if (fastIsInstance(self)) { + Class c = fastClass(self); + + if (c == _fastCls._NXConstantString || + c == _fastCls._NSGCString || + c == _fastCls._NSGMutableCString) { + return _fastImp._NSGCString_isEqual_(self, + @selector(isEqual:), other); + } + if (c == _fastCls._NSGString || + c == _fastCls._NSGMutableString) { + return _fastImp._NSGString_isEqual_(self, + @selector(isEqual:), other); + } + } + return [self isEqual: other]; +} + /* * The 'Fastmap' stuff provides an inline implementation of a mapping * table - for maximum performance. */ +#define FAST_MAP_HASH(X) myHash(X.o) +#define FAST_MAP_EQUAL(X,Y) myEqual(X.o,Y.o) + #include "FastMap.x" @class NSDictionaryNonCore; diff --git a/Source/NSGString.m b/Source/NSGString.m index f9983c2d0..827443b09 100644 --- a/Source/NSGString.m +++ b/Source/NSGString.m @@ -38,25 +38,12 @@ #include /* memcpy(), strlen(), strcmp() are gcc builtin's */ +#include #include @implementation NSGString -static Class immutableClass; -static Class mutableClass; - -+ (void) initialize -{ - static int done = 0; - if (!done) - { - done = 1; - immutableClass = [NSGString class]; - mutableClass = [NSGMutableString class]; - } -} - - (void)dealloc { if (_free_contents) @@ -70,7 +57,7 @@ static Class mutableClass; - (unsigned) hash { if (_hash == 0) - if ((_hash = [super hash]) == 0) + if ((_hash = _fastImp._NSString_hash(self, @selector(hash))) == 0) _hash = 0xffffffff; return _hash; } @@ -80,13 +67,15 @@ static Class mutableClass; Class c; if (anObject == self) return YES; - c = [anObject class]; - if (c == immutableClass || c == mutableClass) + c = fastClassOfInstance(anObject); + if (c == _fastCls._NSGString || c == _fastCls._NSGMutableString) { NSGString *other = (NSGString*)anObject; - if (_hash == 0) [self hash]; - if (other->_hash == 0) [other hash]; + if (_hash == 0) + _fastImp._NSGString_hash(self, @selector(hash)); + if (other->_hash == 0) + _fastImp._NSGString_hash(self, @selector(hash)); if (_hash != other->_hash) return NO; return [self isEqualToString: other]; diff --git a/Source/NSObject.m b/Source/NSObject.m index ff55d9558..15ad5cf5d 100644 --- a/Source/NSObject.m +++ b/Source/NSObject.m @@ -37,6 +37,11 @@ #include #include +#include + +fastCls _fastCls; /* Structure to cache classes. */ +fastImp _fastImp; /* Structure to cache methods. */ + /* * Reference count and memory management @@ -309,6 +314,26 @@ static BOOL double_release_check_enabled = NO; retain_counts_gate = objc_mutex_allocate (); #endif autorelease_class = [NSAutoreleasePool class]; + /* + * Cache some classes for quick access later. + */ + _fastCls._NSString = [NSString class]; + _fastCls._NSGString = [NSGString class]; + _fastCls._NSGMutableString = [NSGMutableString class]; + _fastCls._NSGCString = [NSGCString class]; + _fastCls._NSGMutableCString = [NSGMutableCString class]; + _fastCls._NXConstantString = [NXConstantString class]; + /* + * Cache some method implementations for quick access later. + */ + _fastImp._NSString_hash = (unsigned (*)())[_fastCls._NSString + instanceMethodForSelector: @selector(hash)]; + _fastImp._NSGString_hash = (unsigned (*)())[_fastCls._NSGString + instanceMethodForSelector: @selector(hash)]; + _fastImp._NSGString_isEqual_ = (BOOL (*)())[_fastCls._NSGString + instanceMethodForSelector: @selector(isEqual:)]; + _fastImp._NSGCString_isEqual_ = (BOOL (*)())[_fastCls._NSGCString + instanceMethodForSelector: @selector(isEqual:)]; } return; } diff --git a/Source/NSString.m b/Source/NSString.m index 5a02bcb81..7d9f93b85 100644 --- a/Source/NSString.m +++ b/Source/NSString.m @@ -269,14 +269,14 @@ handle_printf_atsign (FILE *stream, + (NSString*) stringWithCString: (const char*) byteString { - return [[[self alloc] initWithCString:byteString] + return [[[NSString_c_concrete_class alloc] initWithCString:byteString] autorelease]; } + (NSString*) stringWithCString: (const char*)byteString length: (unsigned int)length { - return [[[self alloc] + return [[[NSString_c_concrete_class alloc] initWithCString:byteString length:length] autorelease]; } @@ -1552,15 +1552,34 @@ else } while(notdone); p = source; + while (*p && char_count++ < NSHashStringLength) { +#if 1 + ret = (ret << 5) + ret + *p++; +#else +#if 1 + ret = (ret << 4) + *p++; + ctr = ret & 0xf0000000L; + if (ctr != 0) + ret ^= ctr ^ (ctr >> 24); +#else ret ^= *p++ << ctr; ctr = (ctr + 4) % 20; +#endif +#endif } + + /* + * The hash caching in our concrete strin classes uses zero to denote + * an empty cache value, so we must not return a hash of zero. + */ + if (ret == 0) + ret = 0xffffffff; return ret; } else - return 0; + return 0xfffffffe; /* Hash for an empty string. */ } // Getting a Shared Prefix @@ -2660,15 +2679,17 @@ else + (NSString*) stringWithCString: (const char*)byteString { - return [self stringWithCString:byteString length:strlen(byteString)]; + return [[[NSMutableString_c_concrete_class alloc] + initWithCString:byteString] + autorelease]; } + (NSString*) stringWithCString: (const char*)byteString length: (unsigned int)length { - return [[[self alloc] - initWithCString:byteString length:length] - autorelease]; + return [[[NSMutableString_c_concrete_class alloc] + initWithCString:byteString length:length] + autorelease]; } /* xxx Change this when we have non-CString classes */