Effectively final

Tim Fox timvolpe at gmail.com
Sat Jul 30 10:10:23 PDT 2011


On 30/07/2011 17:55, Rémi Forax wrote:
> On 07/30/2011 04:54 PM, Tim Fox wrote:
>> On 30/07/2011 14:04, Rémi Forax wrote:
>>> On 07/30/2011 09:10 AM, Tim Fox wrote:
>>>> I'm not really sure what you're getting at here. Frameworks like these
>>>> are typically used to write network servers, that support many
>>>> connections, e.g. an HTTP server, the "computation" is in creating the
>>>> response. Nothing should get "stuck". Can you elaborate?
>>> Your HTTP server will deliver services like generating a SHA key,
>>> computing a shortest path, etc.
>>> Sometime the network is the bottleneck but sometime the service
>>> is the bottleneck.
>>>
>>> The reactor pattern works well if the network is the bottleneck
>>> not if the service you deliver takes time to be computed.
>> Ah, you're referring to a small number of connections (perhaps less than
>> number of cores), that are doing long running computations? In such a
>> case, yes, it might make sense to so try to parallelize the computation
>> over multiple threads (e.g. fork/join). However, there's no point in
>> doing that if you've got many connections, all doing long running
>> computations, in that case you may as well do the whole computation on
>> one thread (you'll get less context switching for one thing).
> Doing an async read/write will call poll/epoll which requires
> a context switching between the kernel and the user land.
> So you will do some context switching.
> The reactor pattern works well because you do one
> call to poll/epoll for several read/write.
>
> So if a worker thread does computations for a lot of connections
> before a context switch there will be no problem.
>
>> Number of connections>   number of cores is the normal case, so I
>> consider this an edge case for the kinds of systems people this
>> framework is aimed at. Having said that, if people do want to do this
>> kind of thing, nothing is stopping them sending messages to other
>> contexts and composing the results back asynchronously using the
>> composition facilities that I described earlier. (Similar to fork/join)
> That was my point.
> In that case, you need to send data (messages) to another thread.
Right, and as I said before, we support that :)
>
>>>>> So you will refine again your model saying it's ok to have a context
>>>>> object (a scope)
>>>>> that contains the results of each phases (read, decode, work, encode,
>>>>> write, etc)
>>>>> that is passed from thread to thread because only one thread can access
>>>>> to that context at a time.
>>>> I don't see why that would be necessary.
>>>>> This model restricts side effect to only one object
>>>> It restricts side effects to a set of objects. Typically the set of
>>>> objects is all the objects created by a specific connection. I.e. all
>>>> the objects that comes off a specific connection lives in an "island of
>>>> single-threadedness". Since you typically have many more connections
>>>> than cores, this is how you scale. All callbacks registered in that
>>>> island will subsequently be invoked by the system in the same context
>>>> (i.e. with the same event loop thread). The dev can therefore write
>>>> their code as single threaded.
>>> The question is why your set of objects is stored in the callbacks ?
>>> You have more freedom if you decouple the set of objects and
>>> the callback.
>>> Basically, you only need to ensure that only one thread access
>>> to the set of objects at a time (+ some publication trouble
>>> that comes from the memory model), reusing the same thread
>>> as you do in your framework is a simplification, not a generic model.
>> That's not quite true. It's advantageous to use the exact same thread,
>> rather than just ensure that no more than one thread at any one time
>> accesses the objects, since if it's the same thread each time we don't
>> have to execute any memory barriers (and subsequent cache flushing on
>> multi-core systems) between invocations as we would have to do if we
>> were using multiple threads.
> The kind of memory barrier you need is cheap on recent hardware
> (nehalem, sandy bridge) combined with the fact that an async handler
> is never inlined, it's far from obvious to see the impact of the memory
> barrier
> when you monitor the code.
Fine, but not everyone is running the latest hardware, and it's hard to 
argue that zero memory barriers is not more desirable than more than 
greater than zero memory barriers.
>
>>> If you take a look to the AIO API provided in JDK7,
>>> http://download.oracle.com/javase/7/docs/api/java/nio/channels/AsynchronousByteChannel.html#read%28java.nio.ByteBuffer,%20A,%20java.nio.channels.CompletionHandler%29
>>> a read takes a callback *and* an object (here called attachment) that
>>> encapsulates
>>> your set of objects.
>>> When the callback will be called, you will by example starts a write
>>> sending the same
>>> attachment or a newly created object computed from the value of the
>>> current attachment
>>> as a new attachment.
>>> The write may be done by another thread but it's ok because the two
>>> threads will not access
>>> to the same object at the same time.
>>>
>>> So my point is this pattern is better than the reactor pattern (multi or
>>> not)
>>> because you can transform your data between a read and a write using
>>> a thread-pool. This pattern failed if you modify any state which is
>>> accessible
>>> not from the attachment but by example bound to the callback
>>> so it's better to use lambda here, because lambda aren't object (read
>>> has no field)
>>> and doesn't allow to capture mutable local variable.
>> I'm sorry, I am having trouble parsing this :(
> Basically, it's better to send your context to the async handler instead
> of binding it to the async handler.
Thanks, I understood that much already from your previous explanation. 
What I didn't understand was your line of reasoning that came to that 
conclusion ;)
>
> Rémi
>
>
>



More information about the lambda-dev mailing list