About tiny tiny C code: If I wanted to use a set amount of memory, like unsigned char byteMem[2048] in which to store and manipulate a number of byte queues; my standard C approach would be to create a set of structs the let me pool out chunks of memory from the space for the byte queues to work in. I am not having luck finding references on how to keep the most possible usable space for my queues to use. 2 questions: - What are some good books or other reading on memory efficient C code? - Have I missed some easy optimizations in the following code? The QueueSlot uses 8 bits for a queue identifier and 8 bits for storing a value. Is better memory efficiency just a matter of using more of memory for data storage? Feel free to chop out most of this in replies: I wrote some some C code that is straight forward enough -------------------------------------------------------------------------- /* compactqueues.h */ //A place to store the queues typedef struct { int dataLength; unsigned char* data; } Store; // A memory layout for a queue slot // A tag and a value // This approach is decidely in favor of very dynamic behavior sacrificing some // memory space for tagging. But any queue can easily grow to any size // permitted by the buffer limit. typedef struct { unsigned char slotTag : 8; unsigned char slotValue : 8; } QueueSlot; //A queue in this approach needs to have //a distinct tag value //a headIndex for implicit queue ordering //and access to the buffer typedef struct { unsigned char myTag : 4; int headIndex; Store *storeAccess; } Queue; //This is a convenience collection to pool access to the //management bits typedef struct { Queue *queuePool; unsigned char *queueMarkers; unsigned char poolSize; Store *storeKeeper; } QueueKeeper; // An initialization function // To scrub the store buffer to 0's void initQueuePool(); Queue * create_queue(); void destroy_queue(Queue * q); void enqueue_byte(Queue * q, unsigned char b); unsigned char dequeue_byte(Queue * aQueue); ------------------------------------------------------------------------------------------- /* compactqueues.c */ #include "CompactQueue.h" #include <stdio.h> //#include <time.h> //#define DEFAULT_STORE_SIZE 2048 //unsigned short externStoreSize = DEFAULT_STORE_SIZE; //unsigned char TagGenerator = 0; QueueKeeper* externQueueKeeper; void on_out_of_memory() { fprintf( stdout, "Out of memory! \n" ); while (1) { //dont return? well ok then } } void on_illegal_operation() { fprintf( stdout, "Illegal operation! \n" ); while (1) { //dont return? well ok then } } void initQueuePool() { if ( externQueueKeeper ) { //Scrub the marker slots to 0 for ( short i=0; i< externQueueKeeper->poolSize; ++i ) { externQueueKeeper->queueMarkers[i] = 0; } //Scrub the data slots to 0 if ( externQueueKeeper->storeKeeper ) { Store* localStoreAccess = externQueueKeeper->storeKeeper; unsigned char* store = localStoreAccess->data; if (store) { QueueSlot * asSlot = (QueueSlot*) store; int slotCount = localStoreAccess->dataLength / sizeof(QueueSlot) ; for ( int i=0;i< slotCount; ++i) { asSlot->slotTag = 0; asSlot->slotValue = 0; //fprintf( stdout, "Clear QueueSlots - Slot - %i, Value - %i \n", asSlot->slotTag, asSlot->slotValue ); ++asSlot; } } } } } Queue* create_queue() { if ( !externQueueKeeper ) return 0; short indexToTake = -1; for ( short i=0; i< externQueueKeeper->poolSize; ++i ) { if ( externQueueKeeper->queueMarkers[i] == 0 ) { indexToTake = i; //fprintf(stdout, "indexToTake - %i\n", indexToTake ); break; } } if ( indexToTake > -1 ) { externQueueKeeper->queueMarkers[indexToTake] = 1; externQueueKeeper->queuePool[indexToTake].myTag = indexToTake + 1; externQueueKeeper->queuePool[indexToTake].headIndex = 0; externQueueKeeper->queuePool[indexToTake].storeAccess = externQueueKeeper->storeKeeper; return &(externQueueKeeper->queuePool[indexToTake]); } return 0; } void destroy_queue(Queue * q) { if ( q && q->storeAccess ) { Store* localStoreAccess = q->storeAccess; unsigned char* store = localStoreAccess->data; if (store) { QueueSlot * asSlot = (QueueSlot*) store; int slotCount = localStoreAccess->dataLength / sizeof(QueueSlot) ; int headOffset = q->headIndex; int slotsFreed = 0; for ( int i=0 ;i < slotCount; ++i) { QueueSlot* currentSlot = (asSlot + headOffset%slotCount); if ( currentSlot->slotTag == q->myTag ) { currentSlot->slotTag = 0; currentSlot->slotValue = 0; ++slotsFreed; } } //fprintf( stdout, "Destroying Queue - %i. %i slots left in use are now free.\n", q->myTag, slotsFreed ); //Reset Queue for reuse from the pool. externQueueKeeper->queueMarkers[q->myTag] = 0; q->headIndex = 0; q->myTag = 0; q->storeAccess = 0; //now the queue for pool slot <myTag> can be found for use again } } } void enqueue_byte(Queue * q, unsigned char b) { if ( q && q->storeAccess ) { Store* localStoreAccess = q->storeAccess; unsigned char* store = localStoreAccess->data; if (store) { QueueSlot * asSlot = (QueueSlot*) store; int slotCount = localStoreAccess->dataLength / sizeof(QueueSlot) ; int headOffset = q->headIndex; int slotUsed = -1; for ( int i=0 ;i < slotCount; ++i) { int currentIndex = (i + headOffset%slotCount); QueueSlot* currentSlot = (asSlot + headOffset%slotCount); if ( currentSlot->slotTag == 0 ) { slotUsed = currentIndex; currentSlot->slotTag = q->myTag;// this slot know belongs to the queue passed in currentSlot->slotValue = b; //fprintf( stdout, "Slot %i belongs to queue- %i, with Value - %i \n", currentIndex, currentSlot->slotTag, currentSlot->slotValue ); break; } ++asSlot; } if ( slotUsed == -1 ) { fprintf( stdout, "No Slot open. Enqueue failed \n" ); on_out_of_memory(); } } } } unsigned char dequeue_byte(Queue * q) { unsigned char dequeueduchar = 0; if ( q && q->storeAccess ) { Store* localStoreAccess = q->storeAccess; unsigned char* store = localStoreAccess->data; if ( store ) { QueueSlot * asSlot = (QueueSlot*) store; int slotCount = localStoreAccess->dataLength / sizeof(QueueSlot) ; int headOffset = q->headIndex; unsigned char seeking = 1; //first seek the bit to dequeue, then try to update the headIndex for ( int i=0;i< slotCount; ++i) { int currentIndex = (i + headOffset%slotCount); QueueSlot* currentSlot = (asSlot + headOffset%slotCount); //find the first slot with myTag as the slotTag value //use mod to do a complete loop around starting from the headIndex if ( seeking ) { if ( currentSlot->slotTag == q->myTag ) { dequeueduchar = currentSlot->slotValue; //free the slot for use currentSlot->slotValue = 0; currentSlot->slotTag = 0; q->headIndex = 0; //this will get updated to the true next headIndex if there really is a next one, if not then it should be 0 anyway //fprintf( stdout, "Free slot %i from Queue- %i, returning Value - %i \n", currentIndex, currentSlot->slotTag, dequeueduchar ); seeking = 0; } } else { if ( currentSlot->slotTag == q->myTag ) { q->headIndex = currentIndex; //fprintf( stdout, "Updating headIndex of Queue- %i to slot - %i \n", q->myTag, currentIndex ); return dequeueduchar; } } ++asSlot; } //if ( q->headIndex == 0 ) // fprintf( stdout, "Reseting headIndex of Queue- %i to slot 0 \n", q->myTag ); //if nothing to dequeue for given queue if ( seeking == 1 ) on_illegal_operation(); } } return dequeueduchar; } ------------------------------------------------------------------------------------------- /* main.c - Test it out */ #include <stdio.h> #include "CompactQueue.h" extern QueueKeeper* externQueueKeeper; //extern unsigned short externStoreSize; int main (int argc, const char * argv[]) { Store aStore; const int storeSize = 2048; //aStore.dataLength = externStoreSize; unsigned char storeData[storeSize]; aStore.dataLength = storeSize; aStore.data = storeData; const unsigned char poolSize = 64; //setup the zooKeeper, I mean queueKeeper QueueKeeper aQueueKeeper; Queue aQueuePool[poolSize]; unsigned char queueMarkers[poolSize]; aQueueKeeper.queuePool = aQueuePool; aQueueKeeper.queueMarkers = queueMarkers; aQueueKeeper.poolSize = poolSize; aQueueKeeper.storeKeeper = &aStore; externQueueKeeper = &aQueueKeeper; initQueuePool(); //Queues in action Queue* q1 = create_queue(); Queue* q2 = create_queue(); enqueue_byte(q1, 0); enqueue_byte(q1, 1); enqueue_byte(q2, 3); enqueue_byte(q1, 2); enqueue_byte(q2, 4); printf("%d ", dequeue_byte(q1)); printf("%d\n", dequeue_byte(q1)); enqueue_byte(q1, 5); enqueue_byte(q2, 6); printf("%d ", dequeue_byte(q1)); printf("%d\n", dequeue_byte(q1)); destroy_queue(q1); printf("%d ", dequeue_byte(q2)); printf("%d ", dequeue_byte(q2)); printf("%d\n", dequeue_byte(q2)); destroy_queue(q2); return 0; }
Memory efficient C queues
Started by ●April 10, 2008