hash tidyups and rewrite of array sorting for better performance.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@23967 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2006-10-26 08:33:40 +00:00
parent c1d009c3c1
commit 595be905a3
11 changed files with 180 additions and 173 deletions

View file

@ -1,3 +1,18 @@
2006-10-26 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSPropertyList.m: use declaration of GSArray from GSPrivate.h
* Source/GSArray.m: ditto
* Source/NSArray.m: ditto
* Source/NSSerializer.m ditto
* Source/NSConcreteNumberTemplate.m: Use private function to get hash.
* Source/NSConcreteNumber.m: ditto
* Source/NSNumber.m: ditto
* Source/GSPrivate.h: Add hash functions.
* Headers/Foundation/NSSortDescriptor.h: Add a little documentation.
* Source/NSSortDescriptor.m: Implement better hash/isEqual as
suggested by David. Complete rewrite of sorting to avoid lots of
heap memory operations ... should be much much faster.
2006-10-25 Richard Frith-Macdonald <rfm@gnu.org> 2006-10-25 Richard Frith-Macdonald <rfm@gnu.org>
* configure.ac: Define HAVE_VISIBILITY_ATTRIBUTE if gcc's visibility * configure.ac: Define HAVE_VISIBILITY_ATTRIBUTE if gcc's visibility

View file

@ -63,6 +63,17 @@ extern "C" {
@interface NSMutableArray (NSSortDescriptorSorting) @interface NSMutableArray (NSSortDescriptorSorting)
/**
* This method works like this: first, it sorts the entire
* contents of the array using the first sort descriptor. Then,
* after each sort-run, it looks whether there are sort
* descriptors left to process, and if yes, looks at the partially
* sorted array, finds all portions in it which are equal
* (evaluate to NSOrderedSame) and applies the following
* descriptor onto them. It repeats this either until all
* descriptors have been applied or there are no more equal
* portions (equality ranges) left in the array.
*/
- (void) sortUsingDescriptors: (NSArray *)sortDescriptors; - (void) sortUsingDescriptors: (NSArray *)sortDescriptors;
@end @end

View file

@ -36,6 +36,8 @@
// For private method _decodeArrayOfObjectsForKey: // For private method _decodeArrayOfObjectsForKey:
#include "Foundation/NSKeyedArchiver.h" #include "Foundation/NSKeyedArchiver.h"
#include "GSPrivate.h"
static SEL eqSel; static SEL eqSel;
static SEL oaiSel; static SEL oaiSel;
@ -54,39 +56,10 @@ static Class GSInlineArrayClass;
@interface GSArrayEnumeratorReverse : GSArrayEnumerator @interface GSArrayEnumeratorReverse : GSArrayEnumerator
@end @end
@interface GSArray : NSArray
{
@public
id *_contents_array;
unsigned _count;
}
@end
@interface GSInlineArray : GSArray
{
}
@end
@interface GSMutableArray : NSMutableArray
{
@public
id *_contents_array;
unsigned _count;
unsigned _capacity;
int _grow_factor;
}
@end
@interface GSMutableArray (GSArrayBehavior) @interface GSMutableArray (GSArrayBehavior)
- (void) _raiseRangeExceptionWithIndex: (unsigned)index from: (SEL)sel; - (void) _raiseRangeExceptionWithIndex: (unsigned)index from: (SEL)sel;
@end @end
@interface GSPlaceholderArray : NSArray
{
}
@end
@implementation GSArray @implementation GSArray
- (void) _raiseRangeExceptionWithIndex: (unsigned)index from: (SEL)sel - (void) _raiseRangeExceptionWithIndex: (unsigned)index from: (SEL)sel

View file

@ -48,6 +48,38 @@
#include "GNUstepBase/GSObjCRuntime.h" #include "GNUstepBase/GSObjCRuntime.h"
#include "Foundation/NSArray.h"
@interface GSArray : NSArray
{
@public
id *_contents_array;
unsigned _count;
}
@end
@interface GSMutableArray : NSMutableArray
{
@public
id *_contents_array;
unsigned _count;
unsigned _capacity;
int _grow_factor;
}
@end
@interface GSInlineArray : GSArray
{
}
@end
@interface GSPlaceholderArray : NSArray
{
}
@end
#include "Foundation/NSString.h" #include "Foundation/NSString.h"
/** /**
@ -285,6 +317,34 @@ GSPrivateIsByteEncoding(NSStringEncoding encoding) GS_ATTRIB_PRIVATE;
BOOL BOOL
GSPrivateIsEncodingSupported(NSStringEncoding encoding) GS_ATTRIB_PRIVATE; GSPrivateIsEncodingSupported(NSStringEncoding encoding) GS_ATTRIB_PRIVATE;
/* Hash function to hash up to limit bytes from data of specified length.
* If the flag is NO then a result of 0 is mapped to 0xffffffff.
* This is a pretty useful general purpose hash function.
*/
static inline unsigned
GSPrivateHash(const void *data, unsigned length, unsigned limit, BOOL zero)
__attribute__((unused));
static inline unsigned
GSPrivateHash(const void *data, unsigned length, unsigned limit, BOOL zero)
{
unsigned ret = length;
unsigned l = length;
if (limit < length)
{
l = limit;
}
while (l-- > 0)
{
ret = (ret << 5) + ret + ((const unsigned char*)data)[l];
}
if (ret == 0 && zero == NO)
{
ret = 0xffffffff;
}
return ret;
}
/* load a module into the runtime /* load a module into the runtime
*/ */
long long
@ -307,6 +367,11 @@ void GSPrivateNotifyIdle(void) GS_ATTRIB_PRIVATE;
*/ */
BOOL GSPrivateNotifyMore(void) GS_ATTRIB_PRIVATE; BOOL GSPrivateNotifyMore(void) GS_ATTRIB_PRIVATE;
/* Function to return the hash value for a small integer (used by NSNumber).
*/
unsigned
GSPrivateSmallHash(int n) GS_ATTRIB_PRIVATE;
/* Function to append data to an GSStr /* Function to append data to an GSStr
*/ */
void void

View file

@ -79,19 +79,6 @@ extern void GSPropertyListMake(id,NSDictionary*,BOOL,BOOL,unsigned,id*);
@class GSArray;
@interface GSArray : NSObject // Help the compiler
@end
@class GSInlineArray;
@interface GSInlineArray : NSObject // Help the compiler
@end
@class GSMutableArray;
@interface GSMutableArray : NSObject // Help the compiler
@end
@class GSPlaceholderArray;
@interface GSPlaceholderArray : NSObject // Help the compiler
@end
static Class NSArrayClass; static Class NSArrayClass;
static Class GSArrayClass; static Class GSArrayClass;
static Class GSInlineArrayClass; static Class GSInlineArrayClass;

View file

@ -30,6 +30,7 @@
#include "Foundation/NSException.h" #include "Foundation/NSException.h"
#include "Foundation/NSCoder.h" #include "Foundation/NSCoder.h"
#include "NSConcreteNumber.h" #include "NSConcreteNumber.h"
#include "GSPrivate.h"
#define TYPE_ORDER 0 #define TYPE_ORDER 0
#include "NSConcreteNumberTemplate.m" #include "NSConcreteNumberTemplate.m"

View file

@ -104,7 +104,7 @@
if (data <= GS_SMALL) if (data <= GS_SMALL)
#endif #endif
{ {
return GSSmallHash((int)data); return GSPrivateSmallHash((int)data);
} }
#endif #endif

View file

@ -41,6 +41,7 @@
#include "Foundation/NSObjCRuntime.h" #include "Foundation/NSObjCRuntime.h"
#include "NSConcreteNumber.h" #include "NSConcreteNumber.h"
#include "GSPrivate.h"
@interface GSCachedBool : NSBoolNumber @interface GSCachedBool : NSBoolNumber
@end @end
@ -152,7 +153,7 @@ GSNumberInfoFromObject(NSNumber *o)
} }
unsigned int unsigned int
GSSmallHash(int n) GSPrivateSmallHash(int n)
{ {
return smallHashes[n + GS_SMALL]; return smallHashes[n + GS_SMALL];
} }

View file

@ -50,9 +50,6 @@
extern BOOL GSScanDouble(unichar*, unsigned, double*); extern BOOL GSScanDouble(unichar*, unsigned, double*);
@class GSMutableArray;
@interface GSMutableArray : NSObject // Help the compiler
@end
@class GSMutableDictionary; @class GSMutableDictionary;
@interface GSMutableDictionary : NSObject // Help the compiler @interface GSMutableDictionary : NSObject // Help the compiler
@end @end

View file

@ -49,10 +49,6 @@
@class NSDataMalloc; @class NSDataMalloc;
@interface NSDataMalloc : NSObject // Help the compiler @interface NSDataMalloc : NSObject // Help the compiler
@end @end
@class GSInlineArray;
@class GSMutableArray;
@interface GSMutableArray : NSObject // Help the compiler
@end
/* /*
* Setup for inline operation of string map tables. * Setup for inline operation of string map tables.

View file

@ -30,6 +30,9 @@
#include "Foundation/NSKeyValueCoding.h" #include "Foundation/NSKeyValueCoding.h"
#include "Foundation/NSString.h" #include "Foundation/NSString.h"
#include "GNUstepBase/GSObjCRuntime.h"
#include "GSPrivate.h"
@implementation NSSortDescriptor @implementation NSSortDescriptor
- (BOOL) ascending - (BOOL) ascending
@ -72,7 +75,9 @@
- (unsigned) hash - (unsigned) hash
{ {
return _ascending + (unsigned)(uintptr_t)_selector + [_key hash]; const char *sel = GSNameFromSelector(_selector);
return _ascending + GSPrivateHash(sel, strlen(sel), 16, YES) + [_key hash];
} }
- (id) initWithKey: (NSString *) key ascending: (BOOL) ascending - (id) initWithKey: (NSString *) key ascending: (BOOL) ascending
@ -123,12 +128,7 @@
{ {
return NO; return NO;
} }
/* FIXME ... we should use sel_eq to compare selectors, but if we do if (!sel_eq(((NSSortDescriptor*)other)->_selector, _selector))
* our implementation of -hash will be wrong ... so we will need to
* store all instances in a set to ensure uniqueness.
*/
// if (!sel_eq(((NSSortDescriptor*)other)->_selector, _selector))
if (((NSSortDescriptor*)other)->_selector != _selector)
{ {
return NO; return NO;
} }
@ -238,139 +238,100 @@ SortObjectsWithDescriptor(id *objects,
} }
} }
/**
* Finds all objects in the provided range of the objects array that
* are next to each other and evaluate with the provided sortDescriptor
* as being NSOrderedSame, records their ranges in the provided
* recordedRanges array (enlarging it as necessary) and adjusts the
* numRanges argument to indicate the new size of the range array.
* A pointer to the new location of the array of ranges is returned.
*/
static NSRange *
FindEqualityRanges(id *objects,
NSRange searchRange,
NSSortDescriptor *sortDescriptor,
NSRange *ranges,
unsigned int *numRanges)
{
unsigned int i = searchRange.location;
unsigned int n = NSMaxRange(searchRange);
if (n > 1)
{
while (i < n - 1)
{
unsigned int j;
for (j = i + 1;
j < n &&
[sortDescriptor compareObject: objects[i] toObject: objects[j]]
== NSOrderedSame;
j++);
if (j - i > 1)
{
(*numRanges)++;
ranges = (NSRange *) objc_realloc(ranges, (*numRanges) *
sizeof(NSRange));
ranges[(*numRanges)-1].location = i;
ranges[(*numRanges)-1].length = j - i;
i = j;
}
else
{
i++;
}
}
}
return ranges;
}
@implementation NSArray (NSSortDescriptorSorting) @implementation NSArray (NSSortDescriptorSorting)
- (NSArray *) sortedArrayUsingDescriptors: (NSArray *) sortDescriptors - (NSArray *) sortedArrayUsingDescriptors: (NSArray *) sortDescriptors
{ {
NSMutableArray * sortedArray = [NSMutableArray arrayWithArray: self]; NSMutableArray *sortedArray = [GSMutableArray arrayWithArray: self];
[sortedArray sortUsingDescriptors: sortDescriptors]; [sortedArray sortUsingDescriptors: sortDescriptors];
return [sortedArray makeImmutableCopyOnFail:NO]; return [sortedArray makeImmutableCopyOnFail: NO];
} }
@end @end
/* Sort the objects in range using the first descriptor and, if there
* are more descriptors, recursively call the function to sort each range
* of adhacent equal objects using the remaining descriptors.
*/
static void
SortRange(id *objects, NSRange range, id *descriptors,
unsigned numDescriptors)
{
NSSortDescriptor *sd = (NSSortDescriptor*)descriptors[0];
SortObjectsWithDescriptor(objects, range, sd);
if (numDescriptors > 1)
{
unsigned start = range.location;
unsigned finish = NSMaxRange(range);
while (start < finish)
{
unsigned pos = start + 1;
/* Find next range of adjacent objects.
*/
while (pos < finish
&& [sd compareObject: objects[start]
toObject: objects[pos]] == NSOrderedSame)
{
pos++;
}
/* Sort the range using remaining descriptors.
*/
if (pos - start > 1)
{
SortRange(objects, NSMakeRange(start, pos - start),
descriptors + 1, numDescriptors - 1);
}
start = pos;
}
}
}
@implementation NSMutableArray (NSSortDescriptorSorting) @implementation NSMutableArray (NSSortDescriptorSorting)
/** - (void) sortUsingDescriptors: (NSArray *)sortDescriptors
* This method works like this: first, it sorts the entire
* contents of the array using the first sort descriptor. Then,
* after each sort-run, it looks whether there are sort
* descriptors left to process, and if yes, looks at the partially
* sorted array, finds all portions in it which are equal
* (evaluate to NSOrderedSame) and applies the following
* descriptor onto them. It repeats this either until all
* descriptors have been applied or there are no more equal
* portions (equality ranges) left in the array.
*/
- (void) sortUsingDescriptors: (NSArray *) sortDescriptors
{ {
id *objects; unsigned count = [self count];
unsigned int count; unsigned numDescriptors = [sortDescriptors count];
NSRange *equalityRanges; if (count > 1 && numDescriptors > 0)
unsigned int numEqualityRanges;
unsigned int i, n;
count = [self count];
objects = (id *) objc_calloc(count, sizeof(id));
[self getObjects: objects];
equalityRanges = (NSRange *) objc_calloc(1, sizeof(NSRange));
equalityRanges[0].location = 0;
equalityRanges[0].length = count;
numEqualityRanges = 1;
for (i = 0, n = [sortDescriptors count]; i < n && equalityRanges != NULL; i++)
{ {
unsigned int j; id descriptors[numDescriptors];
NSSortDescriptor *sortDescriptor = [sortDescriptors objectAtIndex: i]; GS_BEGINIDBUF(objects, count);
NSArray *a;
// pass through all equality ranges and sort each of them [self getObjects: objects];
for (j = 0; j < numEqualityRanges; j++) [sortDescriptors getObjects: descriptors];
{ SortRange(objects, NSMakeRange(0, count), descriptors, numDescriptors);
SortObjectsWithDescriptor(objects, equalityRanges[j], a = [[NSArray alloc] initWithObjects: objects count: count];
sortDescriptor); [self setArray: a];
} RELEASE(a);
GS_ENDIDBUF();
// then, if there are sort descriptors left to process }
if (i < n - 1) }
// reconstruct the equality ranges anew.
{ @end
NSRange *newRanges = NULL;
unsigned newNumRanges = 0; @implementation GSMutableArray (NSSortDescriptorSorting)
// process only contents of old equality ranges - (void) sortUsingDescriptors: (NSArray *)sortDescriptors
for (j = 0; j < numEqualityRanges; j++) {
{ unsigned dCount = [sortDescriptors count];
newRanges = FindEqualityRanges(objects, equalityRanges[j],
sortDescriptor, newRanges, &newNumRanges); if (_count > 1 && dCount > 0)
} {
GS_BEGINIDBUF(descriptors, dCount);
objc_free(equalityRanges);
equalityRanges = newRanges; [sortDescriptors getObjects: descriptors];
numEqualityRanges = newNumRanges; SortRange(_contents_array, NSMakeRange(0, _count), descriptors, dCount);
}
GS_ENDIDBUF();
} }
objc_free(equalityRanges);
// now, reconstruct our contents according to the sorted object buffer
[self setArray: [NSArray arrayWithObjects: objects count: count]];
objc_free(objects);
} }
@end @end