Skip to content

Instantly share code, notes, and snippets.

@fjolnir
Created July 4, 2012 03:02
Show Gist options
  • Save fjolnir/3044992 to your computer and use it in GitHub Desktop.
Save fjolnir/3044992 to your computer and use it in GitHub Desktop.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
// the size needed for the batch, with proper alignment for objects
#define ALIGNMENT 8
#define OBJECTS_PER_BUNCH 64
#define BatchSize ((sizeof(TQBatch) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1))
#define PoolSize 128
typedef struct
{
long _instance_size;
NSUInteger _freed;
NSUInteger _allocated;
NSUInteger _reserved;
} TQBatch;
typedef struct
{
NSUInteger poolSize; // Number of batches kept around
uintptr_t low, high;
void *currentBatch;
TQBatch **batches;
} TQBatchPool;
static inline TQBatch *TQNewObjectBatch(TQBatchPool *pool, long batchInstanceSize)
{
NSUInteger len;
NSUInteger nBatches;
unsigned long size;
TQBatch *p;
// Empty pool => allocate new batch
if(pool->low == pool->high) {
batchInstanceSize = (batchInstanceSize + (ALIGNMENT - 1))& ~(ALIGNMENT - 1);
size = batchInstanceSize + sizeof(int);
nBatches = OBJECTS_PER_BUNCH;
len = size * nBatches + BatchSize;
if(!(p = calloc(len, 1))){
NSLog(@"Failed to allocate object. Out of memory?");
return nil;
}
p->_instance_size = batchInstanceSize;
} else {
// Otherwise we recycle an existing batch
p = pool->batches[pool->low];
pool->low = (pool->low + 1) % pool->poolSize;
}
return p;
}
static inline void TQFreeObjectBatch(TQBatchPool *pool, TQBatch *p)
{
uintptr_t next = (pool->high + 1) % pool->poolSize;
if(next == pool->low) // Full?
free(p);
else {
p->_freed = 0;
p->_allocated = 0;
pool->batches[pool->high] = p;
pool->high = next;
}
}
static inline BOOL TQSizeFitsObjectBatch(TQBatch *p, long size)
{
// We can't deal with subclasses larger than what we first allocated
return p && size <= p->_instance_size;
}
static inline BOOL TQBatchIsExhausted(TQBatch *p)
{
return p->_allocated == OBJECTS_PER_BUNCH;
}
#define BATCH_IVARS \
/* The batch the object is in */\
TQBatch *_batch; \
/* It's minus one so we don't have to initialize it to 1 */\
NSInteger _retainCountMinusOne;
#define BATCH_IMPL(Klass) \
static TQBatchPool _BatchPool; \
\
static inline Klass *TQBatchAlloc##Klass(Class self) \
{ \
Klass *obj = nil; \
BOOL flag; \
TQBatchPool *pool = &_BatchPool; \
\
if(!pool->batches) { \
pool->poolSize = PoolSize; \
pool->batches = malloc(sizeof(void*) * pool->poolSize); \
} \
\
/* First time? => Allocate & initialize a new batch */\
size_t instanceSize = class_getInstanceSize(self); \
if(!pool->currentBatch) \
pool->currentBatch = TQNewObjectBatch(pool, instanceSize); \
\
if(TQSizeFitsObjectBatch(pool->currentBatch, instanceSize)) \
{ \
/* Grab an object from the current batch */\
/* and place isa pointer there */\
TQBatch *p = pool->currentBatch; \
NSUInteger offset; \
\
offset = BatchSize + p->_instance_size*p->_allocated; \
obj = (id)((char *)p + offset); \
obj->_batch = p; \
\
p->_allocated++; \
*(Class *)obj = self; \
} \
assert(obj != nil); \
\
/* Batch full? => Make a new one for next time */\
if(TQBatchIsExhausted(pool->currentBatch)) \
pool->currentBatch = TQNewObjectBatch(pool, instanceSize); \
\
return obj; \
} \
\
+ (id)allocWithZone:(NSZone *)zone { return TQBatchAlloc##Klass(self); } \
+ (id)alloc { return TQBatchAlloc##Klass(self); } \
\
- (void)dealloc \
{ \
/* Free the entire batch if all the objects in it are unreferenced */\
if(__sync_add_and_fetch(&_batch->_freed, 1) == OBJECTS_PER_BUNCH) \
TQFreeObjectBatch(&_BatchPool, _batch); \
else if(NO) [super dealloc]; /* Silence compiler warning about not calling super dealloc */\
} \
- (id)retain \
{ \
__sync_add_and_fetch(&_retainCountMinusOne, 1); \
return self; \
} \
- (oneway void)release \
{ \
if(__sync_sub_and_fetch(&_retainCountMinusOne, 1) < 0) \
[self dealloc]; \
}
@interface TQBatchObject : NSObject
{
BATCH_IVARS
}
@end
@implementation TQBatchObject
BATCH_IMPL(TQBatchObject)
@end
int main(int argc, char *argv[]){
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
TQBatchObject *foo;
for(int i = 0; i < 102400000; ++i){
foo = [[TQBatchObject alloc] init];
[foo release];
}
NSLog(@"%@", foo);
[p release];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment