add GSUniqued

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@37812 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2014-04-26 09:26:59 +00:00
parent 298f38c629
commit 000bf4a523
5 changed files with 270 additions and 1 deletions

View file

@ -1,3 +1,12 @@
2014-04-26 Richard Frith-Macdonald <rfm@gnu.org>
* GSUniqued.h:
* GSUniqued.m:
* Performance.h:
* GNUmakefile:
New code to implement uniqued copies of objects for lowered memory
footprint and faster collection lookups.
2013-11-05 Niels Grewe <niels.grewe@halbordnung.de>
* GSFIFO.m: Fix calculation of the timeout for cooperating

View file

@ -43,6 +43,7 @@ Performance_OBJC_FILES += \
GSTicker.m \
GSIndexedSkipList.m \
GSSkipMutableArray.m \
GSUniqued.m \
Performance_HEADER_FILES += \
@ -54,6 +55,7 @@ Performance_HEADER_FILES += \
GSThroughput.h \
GSTicker.h \
GSSkipMutableArray.h \
GSUniqued.h \
Performance_AGSDOC_FILES += \
@ -64,7 +66,8 @@ Performance_AGSDOC_FILES += \
GSThreadPool.h \
GSThroughput.h \
GSTicker.h \
GSSkipMutableArray.h
GSSkipMutableArray.h \
GSUniqued.h \
# Optional Java wrappers for the library

80
GSUniqued.h Normal file
View file

@ -0,0 +1,80 @@
#if !defined(INCLUDED_GSUNIQUED)
#define INCLUDED_GSUNIQUED 1
/**
Copyright (C) 2014 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: April 2014
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import <Foundation/NSObject.h>
/** Class used to unique other objects.<br />
* <p>The point of this class is to lower the memory footprint and speed
* up comparisons (pointer equality) in cases where an application
* stores multiple copies of the same object in various maps.<br />
* Since uniquing is performed by storing an immutable copy of the
* original object in a map until there are no further references
* to that object, it's pointless to use this uniquing unless the
* application would be storing at least two copies of the object.<br />
* Also, since this is thread-safe there is a lock management
* overhead wherever a uniqued object is released, so performance
* gains are to be expected only if the uniqued object has a
* relatively long lifetime and is tested for equality with other
* instances frequently.<br />
* In short, use with care; while uniquing can have a big performance
* advantage for some programs, this is actually quite rare.
* </p>
* <p>The internal implementation of the uniquing works by taking
* immutable copies of the objects to be uniqued, storing those copies
* in a hash table, and swizzling their class pointers to a sub-class
* which will automatically remove the instance from the hash table
* before it is deallocated.<br />
* Access to the hash table is protected by locks so that uniqued
* objects may be used freely in multiple threads.<br />
* The name of the subclass used is the name of the original class
* with 'GSUniqued' added as a prefix.
* </p>
*/
@interface GSUniqued : NSObject
/** This method returns a copy of its argument, uniqued so that other
* such copies of equal objects will be the same instance.<br />
* The argument must respond to -copyWithZone: by returning an instance
* of class of immutable objects (ie where the -hash and -isEqual:
* methods are stable for that instance).
*/
+ (id) copyUniqued: (id<NSObject,NSCopying>)anObject;
@end
/** Category for uniquing any copyable object.<br />
* NB. This must only be used by classes for which -copyWithZone:
* produces an instance of an immutable class.
*/
@interface NSObject (GSUniqued)
/** This method returns a copy of the receiver uniqued so that other
* such copies of equal objects content will be the same instance.
*/
- (id) copyUniqued;
@end
#endif

176
GSUniqued.m Normal file
View file

@ -0,0 +1,176 @@
/**
Copyright (C) 2014 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: April 2014
This file is part of the Performance Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSHashTable.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSSet.h>
#import <GNUstepBase/GSObjCRuntime.h>
#import "GSUniqued.h"
static Class GSUniquedClass = Nil;
static NSLock *uniquedObjectsLock;
static IMP iLock;
static IMP iUnlock;
static NSHashTable *uniquedObjects;
static NSLock *classLock;
static NSMutableDictionary *classMap;
/* Deallocate a uniqued object ... we must remove it from the uniqued
* objects table and then call the real -dealloc method.
*/
static void
uDealloc(id self, SEL _cmd)
{
Class c;
IMP i;
NSHashRemove(uniquedObjects, self);
c = object_getClass(self);
c = class_getSuperclass(c);
i = class_getMethodImplementation(c, _cmd);
(*i)(self, _cmd);
}
/* Release a uniqued object ... we must obtain a lock in case the uniqued
* objects table has to be modified by removal of this instance on
* deallocation.
*/
static void
uRelease(id self, SEL _cmd)
{
Class c;
IMP i;
c = object_getClass(self);
c = class_getSuperclass(c);
i = class_getMethodImplementation(c, _cmd);
(*iLock)(uniquedObjectsLock, @selector(lock));
(*i)(self, _cmd);
(*iUnlock)(uniquedObjectsLock, @selector(unlock));
}
@implementation GSUniqued
+ (void) initialize
{
if (Nil == GSUniquedClass)
{
classLock = [NSLock new];
classMap = [NSMutableDictionary new];
uniquedObjectsLock = [NSLock new];
iLock = [uniquedObjectsLock methodForSelector: @selector(lock)];
iUnlock = [uniquedObjectsLock methodForSelector: @selector(unlock)];
uniquedObjects = NSCreateHashTable(
NSNonRetainedObjectHashCallBacks, 10000);
GSUniquedClass = [GSUniqued class];
}
}
+ (id) allocWithZone: (NSZone*)z
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to allocate instance of GSUniqued"];
return nil;
}
+ (id) copyUniqued: (id<NSObject,NSCopying>)anObject
{
NSObject *found;
NSAssert(nil != anObject, NSInvalidArgumentException);
(*iLock)(uniquedObjectsLock, @selector(lock));
found = [(NSObject*)NSHashGet(uniquedObjects, anObject) retain];
(*iUnlock)(uniquedObjectsLock, @selector(unlock));
if (nil == found)
{
NSObject *aCopy;
Class c;
Class u;
aCopy = [anObject copyWithZone: NSDefaultMallocZone()];
c = object_getClass(aCopy);
[classLock lock];
u = [classMap objectForKey: c];
if (Nil == u)
{
const char *cn = class_getName(c);
char name[strlen(cn) + 20];
Method method;
sprintf(name, "GSUniqued%s", cn);
u = objc_allocateClassPair(c, name, 0);
method = class_getInstanceMethod([NSObject class],
@selector(dealloc));
class_addMethod(u, @selector(dealloc),
(IMP)uDealloc, method_getTypeEncoding(method));
method = class_getInstanceMethod([NSObject class],
@selector(release));
class_addMethod(u, @selector(release),
(IMP)uRelease, method_getTypeEncoding(method));
objc_registerClassPair(u);
[classMap setObject: u forKey: c];
}
[classLock unlock];
(*iLock)(uniquedObjectsLock, @selector(lock));
found = [(NSObject*)NSHashGet(uniquedObjects, anObject) retain];
if (nil == found)
{
found = aCopy;
#if defined(GNUSTEP)
GSClassSwizzle(found, u);
#else
object_setClass(found, u);
#endif
NSHashInsert(uniquedObjects, found);
}
else
{
[aCopy release]; // Already uniqued by another thread
}
(*iUnlock)(uniquedObjectsLock, @selector(unlock));
}
return found;
}
@end
@implementation NSObject (GSUniqued)
- (id) copyUniqued
{
if (Nil == GSUniquedClass) [GSUniqued class];
return [GSUniquedClass copyUniqued: (id<NSObject,NSCopying>)self];
}
@end

View file

@ -32,4 +32,5 @@
#import "GSThroughput.h"
#import "GSTicker.h"
#import "GSSkipMutableArray.h"
#import "GSUniqued.h"