Hi,

Am 21.02.2013 um 18:28 schrieb Niko Matsakis <[email protected]>:

> I think this pattern as you described it could be written with Option<T> 
> types.  What you call Splittable<T> is basically just Option<T>.  To split 
> off you can use the swap_unwrap() method of Option.  You would then use 
> futures or channels to receive back the result and simply assign it (e.g., 
> `event.partA = Some(future.get())`).  


wasn't aware of all the goodies that have been added to Option<T> since I last 
looked :)

Just to be clear: Moving the data out of the Option<T> will even work when the 
Option<T> is embedded inside a uniquely owned struct without causing it to be 
copied?


That would be awesome.


Thanks,


Stefan.


> Stefan Plantikow wrote:
>> 
>> Hi,
>> 
>> 
>> Am 21.02.2013 um 15:21 schrieb Niko Matsakis <[email protected]>:
>> 
>>> The problem with allocating memory in another task is that it's kind of a 
>>> "rendezvous" operation, since you must pause both tasks so that neither 
>>> performs a GC etc during the time of the copy.  Personally I think we 
>>> should stick with serialization unless it becomes a true burden.  We'll 
>>> probably have to improve the serializer to handle cyclic data structures at 
>>> some point though.
>>> 
>>> If there is a true performance problem, we could build an API that allowed 
>>> you to allocate arbitrary data structures in an arena that can then be sent 
>>> to other tasks as a unit.  In this way one could send an arbitrary graph in 
>>> constant time.  This takes a little bit of thought and care and maybe 
>>> require some minor extensions to the type system.
>>> 
>> 
>> 
>> I keep thinking about a feature like that in relationship to the Disruptor 
>> (factory line) concurrency pattern which is one of the fastest methods for 
>> CPU cache-conscious high-speed concurrent processing.
>> 
>> Disruptor summary: A Disruptor is a ring buffer of event objects.   The ring 
>> buffer keeps track of free event slot and events ready for consumption using 
>> two atomically incremented event slot pointers. Events get processed by 
>> different tasks/threads which read directly off the ring buffer and thus 
>> benefit massively from cache locality. Concurrency between tasks is managed 
>> by either ensuring that tasks work on different parts of the event or by 
>> ordering tasks that work on the same part of an event using atomic event 
>> slot pointers (i.e. task b only works on a field 'a' in an event after task 
>> a has worked on it).
>> 
>> I've been wondering how to implement Disruptor in rust without resorting to 
>> using unsafe pointers. To make this convenient, I think it would be 
>> necessary to have partial transfer of ownership of data.  
>> For unique pointers, it should be possible to have that without needing 
>> cycle detecting GC by introducing a "split of" operation that transforms a 
>> unique into the original pointer with access removed to the sub-part (or 
>> replaced with a default value, like option::None), and a new unique pointer 
>> for the torn off sub-part. The second pointer could then be handed off to 
>> another task for parallel processing without incurring copying costs.  The 
>> parent of such a "splittable pointer" would have to keep a reference count 
>> for the torn off part that gets incremented on "split" and decremented on 
>> "union".   When the sub-part goes out of scope, it would automatically 
>> reunion with the parent.  The parent can be freed, when its refcount is 
>> zero. There would be no cross-task cycles since all involved pointers would 
>> still be unique (only owned by a single task at a single point in time). 
>> 
>> The whole point of this would be to have good CPU cache locality, as the 
>> sub-pointer would live at a memory address close to its parent pointer.
>> 
>> I guess what I am thinking about is /something/ like this:
>> 
>> struct Event {
>>      partA: Splittable[A],
>>      partB: Splittable[B]
>> }
>> 
>> fn handleEvent(event: ~Event)
>> {
>>      let (eventWithoutA, partA) = event.partA.split()
>>      
>>      // send unique partA somewhere for processing
>> 
>>      let (eventWithoutB, partB) = eventWithoutA.partB.split()
>>      
>>      // send unique partB somewhere for processing and retrieve it back
>> 
>>      let eventWithUpdatedB = eventWithB.partB.union(receivedPartB)
>> 
>>      // blocking union (?)
>> 
>>      let eventWithUpdatedA = eventWithUpdatedB.partA.blockingUnion()
>> 
>>      // continue
>> }
>> 
>> Is there a better way to achieve this in rust today?
>> 
>> 
>> Cheers,
>> 
>> 
>> Stefan
>> 
>> 
>> 
>> 

_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to