Created
July 4, 2012 03:02
-
-
Save fjolnir/3044992 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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