Efficiency hacks - avoiding objc messaging overheads.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@3032 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
richard 1998-10-06 15:11:27 +00:00
parent 26e47289b1
commit 39b8ff1baf
7 changed files with 386 additions and 88 deletions

151
Headers/gnustep/base/fast.x Normal file
View file

@ -0,0 +1,151 @@
/* Performance enhancing utilities GNUStep
Copyright (C) 1998 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
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 <config.h>
#include <gnustep/base/preface.h>
#include <objc/objc-api.h>
#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);
}

View file

@ -26,6 +26,11 @@
#include <Foundation/NSObject.h> #include <Foundation/NSObject.h>
#include <Foundation/NSZone.h> #include <Foundation/NSZone.h>
/* 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 * This file should be INCLUDED in files wanting to use the FastMap
* functions - these are all declared inline for maximum performance. * functions - these are all declared inline for maximum performance.
@ -86,9 +91,6 @@
#define FAST_MAP_EQUAL(X,Y) [(X).o isEqual: (Y).o] #define FAST_MAP_EQUAL(X,Y) [(X).o isEqual: (Y).o]
#endif #endif
/* To easily un-inline functions for debugging */
#define INLINE inline
typedef union { typedef union {
NSObject *o; NSObject *o;
long int i; long int i;
@ -125,6 +127,9 @@ struct _FastMapTable {
FastMapNode firstNode; /* List for enumerating. */ FastMapNode firstNode; /* List for enumerating. */
size_t bucketCount; /* Number of buckets in map. */ size_t bucketCount; /* Number of buckets in map. */
FastMapBucket buckets; /* Array of buckets. */ 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 { 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 #if FAST_MAP_HAS_VALUE
static INLINE FastMapNode static INLINE FastMapNode
FastMapNewNode(FastMapTable map, FastMapItem key, FastMapItem value) FastMapNewNode(FastMapTable map, FastMapItem key, FastMapItem value)
{ {
FastMapNode node; FastMapNode node = map->freeNodes;
node = (FastMapNode)NSZoneMalloc(map->zone, sizeof(FastMapNode_t)); if (node == 0) {
FastMapMoreNodes(map);
if (node != 0) { node = map->freeNodes;
node->key = key; if (node == 0) {
node->value = value; return 0;
node->nextInBucket = 0; }
node->nextInMap = 0;
} }
map->freeNodes = node->nextInMap;
node->key = key;
node->value = value;
node->nextInBucket = 0;
node->nextInMap = 0;
return node; return node;
} }
#else #else
static INLINE FastMapNode static INLINE FastMapNode
FastMapNewNode(FastMapTable map, FastMapItem key) FastMapNewNode(FastMapTable map, FastMapItem key)
{ {
FastMapNode node; FastMapNode node = map->freeNodes;
node = (FastMapNode)NSZoneMalloc(map->zone, sizeof(FastMapNode_t)); if (node = 0) {
FastMapMoreNodes(map);
if (node != 0) { node = map->freeNodes;
node->key = key; if (node == 0) {
node->nextInBucket = 0; return 0;
node->nextInMap = 0; }
} }
map->freeNodes = node->nextInMap;
node->key = key;
node->nextInBucket = 0;
node->nextInMap = 0;
return node; return node;
} }
#endif #endif
static INLINE void 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 #if FAST_MAP_HAS_VALUE
FAST_MAP_RELEASE_VAL(node->value); FAST_MAP_RELEASE_VAL(node->value);
#endif #endif
NSZoneFree(NSZoneFromPointer(node), node); node->nextInMap = map->freeNodes;
} map->freeNodes = node;
} }
static INLINE FastMapNode static INLINE FastMapNode
@ -312,7 +364,7 @@ FastMapNodeForKey(FastMapTable map, FastMapItem key)
return node; return node;
} }
static INLINE size_t static INLINE void
FastMapResize(FastMapTable map, size_t new_capacity) FastMapResize(FastMapTable map, size_t new_capacity)
{ {
FastMapBucket new_buckets; FastMapBucket new_buckets;
@ -352,12 +404,9 @@ FastMapResize(FastMapTable map, size_t new_capacity)
map->buckets = new_buckets; map->buckets = new_buckets;
map->bucketCount = size; map->bucketCount = size;
} }
/* Return the new capacity. */
return map->bucketCount;
} }
static INLINE size_t static INLINE void
FastMapRightSizeMap(FastMapTable map, size_t capacity) FastMapRightSizeMap(FastMapTable map, size_t capacity)
{ {
/* FIXME: Now, this is a guess, based solely on my intuition. If anyone /* 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 <Albin.L.Jones@Dartmouth.EDU>. */ * L. Jones <Albin.L.Jones@Dartmouth.EDU>. */
if (3 * capacity >= 4 * map->bucketCount) { if (3 * capacity >= 4 * map->bucketCount) {
return FastMapResize(map, (3 * capacity)/4 + 1); FastMapResize(map, (3 * capacity)/4 + 1);
}
else {
return map->bucketCount;
} }
} }
@ -497,7 +543,7 @@ FastMapRemoveKey(FastMapTable map, FastMapItem key)
if (node != 0) { if (node != 0) {
FastMapRemoveNodeFromMap(map, bucket, node); FastMapRemoveNodeFromMap(map, bucket, node);
FastMapFreeNode(node); FastMapFreeNode(map, node);
} }
} }
} }
@ -513,17 +559,28 @@ FastMapEmptyMap(FastMapTable map)
FastMapNode node = bucket->firstNode; FastMapNode node = bucket->firstNode;
FastMapRemoveNodeFromBucket(bucket, node); FastMapRemoveNodeFromBucket(bucket, node);
FastMapFreeNode(node); FastMapFreeNode(map, node);
} }
bucket++; bucket++;
} }
if (map->buckets != 0) { if (map->buckets != 0) {
NSZoneFree(map->zone, map->buckets); 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->firstNode = 0;
map->nodeCount = 0; map->nodeCount = 0;
map->buckets = 0; map->freeNodes = 0;
map->bucketCount = 0; map->zone = 0;
} }
static INLINE FastMapTable static INLINE FastMapTable
@ -534,7 +591,11 @@ FastMapInitWithZoneAndCapacity(FastMapTable map, NSZone *zone, size_t capacity)
map->bucketCount = 0; map->bucketCount = 0;
map->firstNode = 0; map->firstNode = 0;
map->buckets = 0; map->buckets = 0;
map->nodeChunks = 0;
map->freeNodes = 0;
map->chunkCount = 0;
FastMapRightSizeMap(map, capacity); FastMapRightSizeMap(map, capacity);
FastMapMoreNodes(map);
} }

View file

@ -33,25 +33,10 @@
#include <gnustep/base/behavior.h> #include <gnustep/base/behavior.h>
#include <gnustep/base/Unicode.h> #include <gnustep/base/Unicode.h>
#include <gnustep/base/fast.x>
static Class immutableClass;
static Class mutableClass;
static Class constantClass;
@implementation NSGCString @implementation NSGCString
+ (void) initialize
{
static int done = 0;
if (!done)
{
done = 1;
immutableClass = [NSGCString class];
mutableClass = [NSGMutableCString class];
constantClass = [NXConstantString class];
}
}
- (void)dealloc - (void)dealloc
{ {
if (_free_contents) if (_free_contents)
@ -62,7 +47,7 @@ static Class constantClass;
- (unsigned) hash - (unsigned) hash
{ {
if (_hash == 0) if (_hash == 0)
if ((_hash = [super hash]) == 0) if ((_hash = _fastImp._NSString_hash(self, @selector(hash))) == 0)
_hash = 0xffffffff; _hash = 0xffffffff;
return _hash; return _hash;
} }
@ -274,18 +259,19 @@ static Class constantClass;
Class c; Class c;
if (anObject == self) if (anObject == self)
return YES; 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; NSGCString *other = (NSGCString*)anObject;
if (_count != other->_count) if (_count != other->_count)
return NO; return NO;
if (_hash == 0) if (_hash == 0)
if ((_hash = [super hash]) == 0) if ((_hash = _fastImp._NSString_hash(self, @selector(hash))) == 0)
_hash = 0xffffffff; _hash = 0xffffffff;
if (other->_hash == 0) [other hash]; if (other->_hash == 0)
_fastImp._NSGString_hash(other, @selector(hash));
if (_hash != other->_hash) if (_hash != other->_hash)
return NO; return NO;
if (memcmp(_contents_chars, other->_contents_chars, _count) != 0) if (memcmp(_contents_chars, other->_contents_chars, _count) != 0)
@ -302,15 +288,18 @@ static Class constantClass;
{ {
Class c; Class c;
c = [aString class]; c = fastClassOfInstance(aString);
if (c == immutableClass || c == mutableClass || c == constantClass) if (c == _fastCls._NSGCString || c == _fastCls._NSGMutableCString || c == _fastCls._NXConstantString)
{ {
NSGCString *other = (NSGCString*)aString; NSGCString *other = (NSGCString*)aString;
if (_count != other->_count) if (_count != other->_count)
return NO; return NO;
if (_hash == 0) [self hash]; if (_hash == 0)
if (other->_hash == 0) [other hash]; 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) if (_hash != other->_hash)
return NO; return NO;
if (memcmp(_contents_chars, other->_contents_chars, _count) != 0) if (memcmp(_contents_chars, other->_contents_chars, _count) != 0)

View file

@ -28,10 +28,72 @@
#include <Foundation/NSString.h> #include <Foundation/NSString.h>
#include <gnustep/base/Coding.h> #include <gnustep/base/Coding.h>
#include <gnustep/base/fast.x>
/*
* 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 * The 'Fastmap' stuff provides an inline implementation of a mapping
* table - for maximum performance. * 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" #include "FastMap.x"
@class NSDictionaryNonCore; @class NSDictionaryNonCore;

View file

@ -38,25 +38,12 @@
#include <gnustep/base/NSGSequence.h> #include <gnustep/base/NSGSequence.h>
/* memcpy(), strlen(), strcmp() are gcc builtin's */ /* memcpy(), strlen(), strcmp() are gcc builtin's */
#include <gnustep/base/fast.x>
#include <gnustep/base/Unicode.h> #include <gnustep/base/Unicode.h>
@implementation NSGString @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 - (void)dealloc
{ {
if (_free_contents) if (_free_contents)
@ -70,7 +57,7 @@ static Class mutableClass;
- (unsigned) hash - (unsigned) hash
{ {
if (_hash == 0) if (_hash == 0)
if ((_hash = [super hash]) == 0) if ((_hash = _fastImp._NSString_hash(self, @selector(hash))) == 0)
_hash = 0xffffffff; _hash = 0xffffffff;
return _hash; return _hash;
} }
@ -80,13 +67,15 @@ static Class mutableClass;
Class c; Class c;
if (anObject == self) if (anObject == self)
return YES; return YES;
c = [anObject class]; c = fastClassOfInstance(anObject);
if (c == immutableClass || c == mutableClass) if (c == _fastCls._NSGString || c == _fastCls._NSGMutableString)
{ {
NSGString *other = (NSGString*)anObject; NSGString *other = (NSGString*)anObject;
if (_hash == 0) [self hash]; if (_hash == 0)
if (other->_hash == 0) [other hash]; _fastImp._NSGString_hash(self, @selector(hash));
if (other->_hash == 0)
_fastImp._NSGString_hash(self, @selector(hash));
if (_hash != other->_hash) if (_hash != other->_hash)
return NO; return NO;
return [self isEqualToString: other]; return [self isEqualToString: other];

View file

@ -37,6 +37,11 @@
#include <Foundation/NSZone.h> #include <Foundation/NSZone.h>
#include <limits.h> #include <limits.h>
#include <gnustep/base/fast.x>
fastCls _fastCls; /* Structure to cache classes. */
fastImp _fastImp; /* Structure to cache methods. */
/* /*
* Reference count and memory management * Reference count and memory management
@ -309,6 +314,26 @@ static BOOL double_release_check_enabled = NO;
retain_counts_gate = objc_mutex_allocate (); retain_counts_gate = objc_mutex_allocate ();
#endif #endif
autorelease_class = [NSAutoreleasePool class]; 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; return;
} }

View file

@ -269,14 +269,14 @@ handle_printf_atsign (FILE *stream,
+ (NSString*) stringWithCString: (const char*) byteString + (NSString*) stringWithCString: (const char*) byteString
{ {
return [[[self alloc] initWithCString:byteString] return [[[NSString_c_concrete_class alloc] initWithCString:byteString]
autorelease]; autorelease];
} }
+ (NSString*) stringWithCString: (const char*)byteString + (NSString*) stringWithCString: (const char*)byteString
length: (unsigned int)length length: (unsigned int)length
{ {
return [[[self alloc] return [[[NSString_c_concrete_class alloc]
initWithCString:byteString length:length] initWithCString:byteString length:length]
autorelease]; autorelease];
} }
@ -1552,15 +1552,34 @@ else
} while(notdone); } while(notdone);
p = source; p = source;
while (*p && char_count++ < NSHashStringLength) 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; ret ^= *p++ << ctr;
ctr = (ctr + 4) % 20; 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; return ret;
} }
else else
return 0; return 0xfffffffe; /* Hash for an empty string. */
} }
// Getting a Shared Prefix // Getting a Shared Prefix
@ -2660,15 +2679,17 @@ else
+ (NSString*) stringWithCString: (const char*)byteString + (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 + (NSString*) stringWithCString: (const char*)byteString
length: (unsigned int)length length: (unsigned int)length
{ {
return [[[self alloc] return [[[NSMutableString_c_concrete_class alloc]
initWithCString:byteString length:length] initWithCString:byteString length:length]
autorelease]; autorelease];
} }
/* xxx Change this when we have non-CString classes */ /* xxx Change this when we have non-CString classes */