On Friday, January 10, 2014 1:34:54 PM UTC+13, Rajiv Kurian wrote:
>
> I think this is a great solution where the MAX_MSG_SIZE is reasonable and 
> there is not much variance between requests. I am accepting requests to 
> process images/videos. The variance is massive and the maximum size is way 
> too big (100s of MB). Another problem is that the network buffers get 
> filled in asynchronously. So if client-1 connects and wants to send 1 MB of 
> data, I can get a slot in a ring buffer and start filling in the buffer. In 
> the meantime another client could connect and start writing data. Now 
> client-2 could connect and actually send it's data faster than the first 
> one, but we have to wait on the first one to publish the second one. The 
> first one could actually just become unresponsive and could need to be 
> killed by a timer or something. In general a slow client that starts 
> sending a request before other faster clients could stall the pipeline. A 
> pointer to the buffer, while definitely slower than an inline one avoids 
> these issues.
>
 
You can just have separate queues that work cooperatively to resolve this.  
For example, create a freelist queue that is preloaded with a large number 
of small-to-medium preallocated buffers (with extra space for a linked list 
pointer), and a work queue that is initially empty.  When your network 
thread starts receiving data, pop a buffer off the freelist and start 
filling it; once it is full, pop another buffer off the freelist and link 
it to the first (via a linked-list-style pointer).  Once all the data is 
received, queue the linked list of buffers (aka, just the pointer to the 
head buffer) onto your work queue.
 
Your processing thread(s) can then dequeue from the work queue, and when 
they're done with the buffers just push them back onto the freelist.  No 
need to worry about sequence ids or the order in which work arrives this 
way -- although you will still need to deal with timeouts and with running 
out of buffers -- and of course selecting the correct types of queues 
depending on how many threads you have on each end.
 

>  
>
>>
>> #define QUEUE_SIZE (1<<10) 
>>
>> struct element { 
>>   char buf[MAX_MSG_SIZE]; 
>>   ... other data 
>> }; 
>>
>> queue<QUEUE_SIZE, element> q; 
>>
>> This consumes 64M of memory persistently.  But it is as simple and as 
>> fast as you can get. 
>>
>> You will need to "split" enqueue and dequeue functions into 2 parts: 
>> first waits when the next element becomes ready for 
>> production/consumption, and the second part "commits" 
>> production/consumption. 
>> Producer workflow: 
>> - wait till the next element becomes ready for production (it's most 
>> likely already ready, because the queue if FIFO) 
>> - fill in the element (the buffer is already there, right in the element) 
>> - commit production (make the element ready for consumption) 
>> - repeat 
>> Consumer workflow is symmetric: 
>> - wait till the next element becomes ready for consumption 
>> - process the element (don't need to copy the buffer, read it right in 
>> place) 
>> - commit consumption (make the element ready for subsequent production) 
>>
> Right this is what I am doing besides the extra logic to put buffers back 
> into the pool when I detect a need for buffers. 
>

-- 

--- 
You received this message because you are subscribed to the Google Groups 
"Scalable Synchronization Algorithms" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/lock-free/c11a23e8-a5a5-4acd-856e-cc0668c8c8ab%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to