Advertisement
homer512

Queue benchmark

Aug 24th, 2015
132
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.11 KB | None | 0 0
  1. #ifdef __cplusplus
  2. # include <deque>
  3. /* using std::deque */
  4. # include <new>
  5. /* using placement new operator */
  6. #endif
  7.  
  8. #include <stdlib.h>
  9. /* using malloc, free, rand */
  10. #include <time.h>
  11. /* using clock_gettime */
  12. #include <stdio.h>
  13. /* using printf, puts */
  14. #include <errno.h>
  15. /* using errno */
  16. #include <string.h>
  17. /* using memcpy */
  18.  
  19.  
  20. /** Abstract object for different queue implementations */
  21. struct QueueObject;
  22.  
  23. /** Class metadata for queue implementations */
  24. struct QueueInterface
  25. {
  26.   /** Human-readable class name */
  27.   const char* name;
  28.   /** Allocation size */
  29.   size_t size;
  30.   /** Constructor. Returns 0 on success */
  31.   int (*init)(struct QueueObject*);
  32.   /** Destructor. Does not free the object pointer itself */
  33.   void (*free)(struct QueueObject*);
  34.   /** Adds the given pointer to the end of the queue. Returns 0 on success */
  35.   int (*enqueue)(struct QueueObject*, void*);
  36.   /** Returns the first object in the queue or NULL if empty */
  37.   void* (*dequeue)(struct QueueObject*);
  38. };
  39.  
  40.  
  41. #ifdef __cplusplus
  42.  
  43. typedef std::deque<void*> _cppqueue_deque_type;
  44.  
  45. /** C-Wrapper around C++'s STL deque */
  46. struct CppQueue
  47. {
  48.   _cppqueue_deque_type deque;
  49. };
  50.  
  51. static int cppqueue_init(struct QueueObject* vself)
  52. {
  53.   struct CppQueue* self = (struct CppQueue*) vself;
  54.   new (&self->deque) _cppqueue_deque_type;
  55.   return 0;
  56. }
  57.  
  58. static void cppqueue_free(struct QueueObject* vself)
  59. {
  60.   struct CppQueue* self = (struct CppQueue*) vself;
  61.   self->deque.~_cppqueue_deque_type();
  62. }
  63.  
  64. static int cppqueue_enqueue(struct QueueObject* vself, void* element)
  65. {
  66.   struct CppQueue* self = (struct CppQueue*) vself;
  67.   try {
  68.     self->deque.push_back(element);
  69.   } catch(...) {
  70.     return 1;
  71.   }
  72.   return 0;
  73. }
  74.  
  75. static void* cppqueue_dequeue(struct QueueObject* vself)
  76. {
  77.   struct CppQueue* self = (struct CppQueue*) vself;
  78.   void* element = NULL;
  79.   if(self->deque.empty())
  80.     return element;
  81.   element = self->deque.front();
  82.   self->deque.pop_front();
  83.   return element;
  84. }
  85.  
  86. static const struct QueueInterface CPP_QUEUE_INTERFACE = {
  87.   "CppQueue",
  88.   sizeof(struct CppQueue),
  89.   cppqueue_init,
  90.   cppqueue_free,
  91.   cppqueue_enqueue,
  92.   cppqueue_dequeue
  93. };
  94.  
  95. #endif /* __cplusplus */
  96.  
  97. /** Queue based on an exponentially growing array treated as a ring buffer */
  98. struct Queue
  99. {
  100.   /** Points to beginning of the array or NULL if not yet allocated */
  101.   void** ring;
  102.   /**
  103.    * Points to the first element in the queue
  104.    *
  105.    * If it points to writepos, the queue is empty
  106.    */
  107.   void** readpos;
  108.   /** Points behind the last entry in the queue
  109.    *
  110.    * If it points to readpos - 1, the queue is full. Note that this means that
  111.    * one element in the array can never be used
  112.    */
  113.   void** writepos;
  114.   /**
  115.    * Points behind the end of the ring array
  116.    *
  117.    * If readpos or writepos reach this position, they have to wrap around to
  118.    * the beginning
  119.    */
  120.   void** ringend;
  121. };
  122.  
  123. static int queue_init(struct QueueObject* vself)
  124. {
  125.   struct Queue* self = (struct Queue*) vself;
  126.   /** Allocation happens on first access */
  127.   self->ring = self->readpos = self->writepos = self->ringend = NULL;
  128.   return 0;
  129. }
  130.  
  131. static void queue_free(struct QueueObject* vself)
  132. {
  133.   struct Queue* self = (struct Queue*) vself;
  134.   free(self->ring);
  135. }
  136.  
  137. /** Returns the number of elements in the queue */
  138. static size_t _queue_count(const struct Queue* self)
  139. {
  140.   if(self->writepos >= self->readpos)
  141.     return self->writepos - self->readpos;
  142.   else
  143.     return (self->ringend - self->readpos) + (self->writepos - self->ring);
  144. }
  145.  
  146. /** Grows the size of the ring buffer. Returns 0 on success */
  147. static int _queue_extend(struct Queue* self)
  148. {
  149.   size_t newsize;
  150.   void** newring;
  151.   size_t count;
  152.   newsize = (self->ringend - self->ring + 1) * 2;
  153.   if(! (newring = (void**) malloc(newsize * sizeof(void*))))
  154.     return 1;
  155.   count = _queue_count(self);
  156.   if(self->writepos >= self->readpos)
  157.     self->readpos = (void**) memcpy(newring, self->readpos,
  158.                     count * sizeof(void*));
  159.   else {
  160.     size_t before_end = self->ringend - self->readpos;
  161.     self->readpos = (void**) memcpy(newring, self->readpos,
  162.                     before_end * sizeof(void*));
  163.     memcpy(newring + before_end, self->ring,
  164.        (count - before_end) * sizeof(void*));
  165.   }
  166.   free(self->ring);
  167.   self->ring = newring;
  168.   self->writepos = newring + count;
  169.   self->ringend = newring + newsize;
  170.   return 0;
  171. }
  172.  
  173. static int queue_enqueue(struct QueueObject* vself, void* element)
  174. {
  175.   struct Queue* self = (struct Queue*) vself;
  176.   if(self->writepos == self->readpos - 1 || ! self->writepos) {
  177.     int err;
  178.     if((err = _queue_extend(self)) != 0)
  179.       return err;
  180.   }
  181.   *self->writepos = element;
  182.   if(++self->writepos == self->ringend)
  183.     self->writepos = self->ring;
  184.   return 0;
  185. }
  186.  
  187. void* queue_peek(const struct Queue* self)
  188. {
  189.   return self->readpos == self->writepos ? NULL : *self->readpos;
  190. }
  191.  
  192. static void* queue_dequeue(struct QueueObject* vself)
  193. {
  194.   struct Queue* self = (struct Queue*) vself;
  195.   void* element = NULL;
  196.   if(self->readpos == self->writepos)
  197.     return element;
  198.   element = *self->readpos;
  199.   if(++self->readpos == self->ringend)
  200.     self->readpos = self->ring;
  201.   return element;
  202. }
  203.  
  204. static const struct QueueInterface AQUEUE_INTERFACE = {
  205.   "Queue",
  206.   sizeof(struct Queue),
  207.   queue_init,
  208.   queue_free,
  209.   queue_enqueue,
  210.   queue_dequeue
  211. };
  212.  
  213.  
  214. /** Single linked list entry class */
  215. struct LinkedElement
  216. {
  217.   /** next element in list or NULL */
  218.   struct LinkedElement* next;
  219.   /** Whatever the node points to */
  220.   void* element;
  221. };
  222.  
  223. /** Single linked header */
  224. struct SLList
  225. {
  226.   /** First and last nodes in the list or NULL if list is empty */
  227.   struct LinkedElement* front, *back;
  228. };
  229.  
  230. /** Initializes an empty list */
  231. static int sllist_init(struct SLList* self)
  232. {
  233.   self->front = self->back = NULL;
  234.   return 0;
  235. }
  236.  
  237. /** Deallocates all nodes in the list */
  238. static void sllist_free(struct SLList* self)
  239. {
  240.   struct LinkedElement* cur, *next;
  241.   for(cur = self->front; cur != NULL; cur = next) {
  242.     next = cur->next;
  243.     free(cur);
  244.   }
  245. }
  246.  
  247. /** Adds a single node to the end of the list */
  248. static void sllist_push_back(struct SLList* self, struct LinkedElement* node)
  249. {
  250.   node->next = NULL;
  251.   if(self->back)
  252.     self->back->next = node;
  253.   else
  254.     self->front = node;
  255.   self->back = node;
  256. }
  257.  
  258. /** Adds a single node to the front of the list */
  259. static void sllist_push_front(struct SLList* self, struct LinkedElement* node)
  260. {
  261.   if(! (node->next = self->front))
  262.     self->back = node;
  263.   self->front = node;
  264. }
  265.  
  266. /**
  267.  * Allocates a new node and adds it to the back of the list
  268.  *
  269.  * Returns NULL on error
  270.  */
  271. static struct LinkedElement* sllist_alloc_back(struct SLList* self)
  272. {
  273.   struct LinkedElement* node;
  274.   if(! (node = (struct LinkedElement*) malloc(sizeof(struct LinkedElement))))
  275.     return node;
  276.   sllist_push_back(self, node);
  277.   return node;
  278. }
  279.  
  280. /**
  281.  * Removes the first element from the list and returns it
  282.  *
  283.  * Returns NULL if empty
  284.  */
  285. static struct LinkedElement* sllist_pop_front(struct SLList* self)
  286. {
  287.   struct LinkedElement* node;
  288.   if(! (node = self->front))
  289.     return node;
  290.   if(! (self->front = node->next))
  291.     self->back = NULL;
  292.   return node;
  293. }
  294.  
  295.  
  296. /**
  297.  * Queue implementation based on a single linked list
  298.  *
  299.  * Each enqueue results in an malloc
  300.  */
  301. struct SLQueue
  302. {
  303.   struct SLList list;
  304. };
  305.  
  306. static int slqueue_init(struct QueueObject* vself)
  307. {
  308.   struct SLQueue* self = (struct SLQueue*) vself;
  309.   return sllist_init(&self->list);
  310. }
  311.  
  312. static void slqueue_free(struct QueueObject* vself)
  313. {
  314.   struct SLQueue* self = (struct SLQueue*) vself;
  315.   sllist_free(&self->list);
  316. }
  317.  
  318. static int slqueue_enqueue(struct QueueObject* vself, void* element)
  319. {
  320.   struct SLQueue* self = (struct SLQueue*) vself;
  321.   struct LinkedElement* node;
  322.   if(! (node = sllist_alloc_back(&self->list)))
  323.     return 1;
  324.   node->element = element;
  325.   return 0;
  326. }
  327.  
  328. void* slqueue_peek(const struct SLQueue* self)
  329. {
  330.   return self->list.front ? self->list.front->element : NULL;
  331. }
  332.  
  333. static void* slqueue_dequeue(struct QueueObject* vself)
  334. {
  335.   struct SLQueue* self = (struct SLQueue*) vself;
  336.   struct LinkedElement* node;
  337.   void* element = NULL;
  338.   if(! (node = sllist_pop_front(&self->list)))
  339.     return element;
  340.   element = node->element;
  341.   free(node);
  342.   return element;
  343. }
  344.  
  345. static const struct QueueInterface SLQUEUE_INTERFACE = {
  346.   "SLQueue",
  347.   sizeof(struct SLQueue),
  348.   slqueue_init,
  349.   slqueue_free,
  350.   slqueue_enqueue,
  351.   slqueue_dequeue
  352. };
  353.  
  354.  
  355. /**
  356.  * Queue implementation based on a single linked list
  357.  *
  358.  * Allocations are cached in a second list to be reused
  359.  */
  360. struct CSLQueue
  361. {
  362.   struct SLList contained, freelist;
  363. };
  364.  
  365. static int cslqueue_init(struct QueueObject* vself)
  366. {
  367.   struct CSLQueue* self = (struct CSLQueue*) vself;
  368.   struct SLList* lists[] = {&self->contained, &self->freelist};
  369.   int err, i, _errno;
  370.   for(i = err = 0; i < 2 && ! err; ++i)
  371.     err = sllist_init(lists[i]);
  372.   if(! err)
  373.     return err;
  374.   _errno = errno;
  375.   for(--i; i >= 0; --i)
  376.     sllist_free(lists[i]);
  377.   errno = _errno;
  378.   return err;
  379. }
  380.  
  381. static void cslqueue_free(struct QueueObject* vself)
  382. {
  383.   struct CSLQueue* self = (struct CSLQueue*) vself;
  384.   struct SLList* lists[] = {&self->contained, &self->freelist};
  385.   int i;
  386.   for(i = 0; i < 2; ++i)
  387.     sllist_free(lists[i]);
  388. }
  389.  
  390. static int cslqueue_enqueue(struct QueueObject* vself, void* element)
  391. {
  392.   struct CSLQueue* self = (struct CSLQueue*) vself;
  393.   struct LinkedElement* node;
  394.   if(! (node = sllist_pop_front(&self->freelist))) {
  395.     if(! (node = sllist_alloc_back(&self->contained)))
  396.       return 1;
  397.   }
  398.   else
  399.     sllist_push_back(&self->contained, node);
  400.   node->element = element;
  401.   return 0;
  402. }
  403.  
  404. void* cslqueue_peek(const struct CSLQueue* self)
  405. {
  406.   return self->contained.front ? self->contained.front->element : NULL;
  407. }
  408.  
  409. static void* cslqueue_dequeue(struct QueueObject* vself)
  410. {
  411.   struct CSLQueue* self = (struct CSLQueue*) vself;
  412.   struct LinkedElement* node;
  413.   void* element = NULL;
  414.   if(! (node = sllist_pop_front(&self->contained)))
  415.     return element;
  416.   element = node->element;
  417.   sllist_push_front(&self->freelist, node);
  418.   return element;
  419. }
  420.  
  421. static const struct QueueInterface CSLQUEUE_INTERFACE = {
  422.   "CSLQueue",
  423.   sizeof(struct CSLQueue),
  424.   cslqueue_init,
  425.   cslqueue_free,
  426.   cslqueue_enqueue,
  427.   cslqueue_dequeue
  428. };
  429.  
  430.  
  431. /**
  432.  * Dummy queue that does nothing. Used to get base values for the benchmark
  433.  */
  434. struct NoQueue
  435. {
  436.   char dummy;
  437. };
  438.  
  439. static int noqueue_init(struct QueueObject* vself)
  440. { return 0; }
  441.  
  442. static void noqueue_free(struct QueueObject* vself)
  443. {}
  444.  
  445. static int noqueue_enqueue(struct QueueObject* vself, void* unused)
  446. { return 0; }
  447.  
  448. static void* noqueue_dequeue(struct QueueObject* vself)
  449. { return vself; }
  450.  
  451. static const struct QueueInterface NOQUEUE_INTERFACE = {
  452.   "NoQueue",
  453.   sizeof(struct NoQueue),
  454.   noqueue_init,
  455.   noqueue_free,
  456.   noqueue_enqueue,
  457.   noqueue_dequeue
  458. };
  459.  
  460.  
  461. /**
  462.  * Returns 0 if the current operation should be stopped
  463.  *
  464.  * \param flippow scales the probability of a true result by its power of two
  465.  */
  466. static int continue_operation(unsigned flippow)
  467. {
  468.   return rand() & ((1 << flippow) - 1);
  469. }
  470.  
  471. int main()
  472. {
  473.   /* queue implementations to be tested */
  474.   static const struct QueueInterface* interfaces[] = {
  475. #  ifdef __cplusplus
  476.     &CPP_QUEUE_INTERFACE,
  477. #  endif
  478.     &AQUEUE_INTERFACE,
  479.     &SLQUEUE_INTERFACE,
  480.     &CSLQUEUE_INTERFACE,
  481.     &NOQUEUE_INTERFACE
  482.   };
  483.   /* Churn defines how often filling and clearing the queue alternate.
  484.    * High churn values mean there is a high fluctuation in size of the queue
  485.    */
  486.   unsigned churn;
  487.   puts("Churn\tImplementation\tTime per op[s]");
  488.   for(churn = 1; churn <= 10; ++churn) {
  489.     size_t i;
  490.     for(i = 0; i < sizeof(interfaces) / sizeof(*interfaces); ++i) {
  491.       const struct QueueInterface* interface;
  492.       struct QueueObject* obj;
  493.       int err = 0;
  494.       struct timespec starttime, endtime;
  495.       double spenttime, time_per_op;
  496.       int j;
  497.       /* ops counts the enqueue, dequeue operations */
  498.       unsigned ops = 0;
  499.       interface = interfaces[i];
  500.       if(! (obj = (struct QueueObject*) malloc(interface->size))) {
  501.     err = 1;
  502.     goto rtrn;
  503.       }
  504.       if((err = interface->init(obj)) != 0)
  505.     goto obj_free;
  506.       if((err = clock_gettime(CLOCK_MONOTONIC, &starttime)) != 0)
  507.     goto obj_dtor;
  508.       /** Do a few thousand enque, dequeue iterations */
  509.       for(j = 0; j < ((1<<26) >> churn); ++j) {
  510.     do {
  511.       ++ops;
  512.       if(interface->enqueue(obj, obj))
  513.         break;
  514.     } while(continue_operation(churn));
  515.     do {
  516.       ++ops;
  517.       if(! interface->dequeue(obj))
  518.         break;
  519.     } while(continue_operation(churn));
  520.       }
  521.       if((err = clock_gettime(CLOCK_MONOTONIC, &endtime)) != 0)
  522.     goto obj_dtor;
  523.       spenttime = difftime(endtime.tv_sec, starttime.tv_sec)
  524.     + (endtime.tv_nsec - starttime.tv_nsec) * 1e-9;
  525.       time_per_op = spenttime / ops;
  526.       if(printf("%u\t%-14s\t%#g\n", 1 << churn, interface->name, time_per_op)
  527.      < 0) {
  528.     err = 1;
  529.     goto obj_dtor;
  530.       }
  531.     obj_dtor:
  532.       interface->free(obj);
  533.     obj_free:
  534.       free(obj);
  535.     rtrn:
  536.       if(err)
  537.     return err;
  538.     }
  539.   }
  540.   return 0;
  541. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement