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 Frith-MacDonald 1998-10-06 15:11:27 +00:00
parent 37b25563c6
commit 1b0bf3c664
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/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
* 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 <Albin.L.Jones@Dartmouth.EDU>. */
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);
}

View file

@ -33,25 +33,10 @@
#include <gnustep/base/behavior.h>
#include <gnustep/base/Unicode.h>
static Class immutableClass;
static Class mutableClass;
static Class constantClass;
#include <gnustep/base/fast.x>
@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)

View file

@ -28,10 +28,72 @@
#include <Foundation/NSString.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
* 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;

View file

@ -38,25 +38,12 @@
#include <gnustep/base/NSGSequence.h>
/* memcpy(), strlen(), strcmp() are gcc builtin's */
#include <gnustep/base/fast.x>
#include <gnustep/base/Unicode.h>
@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];

View file

@ -37,6 +37,11 @@
#include <Foundation/NSZone.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
@ -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;
}

View file

@ -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 */