How to use Async and Await ?

Using (or providing) Microsoft.NET Classes

Re: How to use Async and Await ?

Postby Morten|Dyalog on Tue Jan 12, 2016 8:06 am

PGilbert wrote:For those interested in TCP/IP, UDP/IP and Serial communication, I contributed three classes to the Wiki.


Thanks for making the effort!

We are considering adding UDP support to Conga, and beefing up good old ⎕ARBIN for more modern serial communications tasks. If we added the ability to explicitly create futures without using isolates (I think the Computer Science folks might call this a "promise"), perhaps we could simplify everyone's lives. It seems painful to have to use the .NET framework for asynchronous programming, when we almost have the language features in Dyalog APL - and could make them portable across all Dyalog platforms.

If there are any features you think we are likely to miss (we will study your classes carefully), please let me know.

P.S. This is not a promise to deliver anything at any particular time, just to let you know we are aware of the issue and it seems to be getting more important.
User avatar
Morten|Dyalog
 
Posts: 315
Joined: Tue Sep 09, 2008 3:52 pm

Re: How to use Async and Await ?

Postby Tomas Gustafsson on Wed Jan 13, 2016 5:18 am

Morten|Dyalog wrote:We are considering adding UDP support to Conga [...] It seems painful to have to use the .NET framework for asynchronous programming, when we almost have the language features in Dyalog APL - and could make them portable across all Dyalog platforms.


I'd really wish you enlarged the UDP ⎕-functions for async behaviour instead :-).

Opinions on UDP, async and forks:

The ws is kind of a holy thing, and invoking an additional, large set of code (Conga), just to get async transmission doesn't seem like the optimal way to go. At least not if you manage the transmission yourself.

To keep in mind: UDP send and receive are inherently two different things! They have nothing to do with each other. UDP is a loose cannon. You send it and forget it. You may however later on expect to get some sort of reply.

1. Send
Windows has two ways to send UDP:

    - socket.Send (blocking), or
    - socket.BeginSend->(callback function)->socket.EndSend (non-blocking)
In the 1st case, Send returns the transmission result (how many bytes were sent, or error), in the 2nd case EndSend does it. Afaik, APL executes the callback in a separate APL thread.

The nature of sending is that you don't repeatedly ask "was it sent, was it sent?". Instead you want someone to call you and tell you it was sent, in which case you can inform the user, or you continue with the next thing. And even so, it has practically no relevance if the send routine announces "it was sent", the only thing that matters is that it was received, accepted & understood and THAT is not a part of UDP automation!

"It was sent" is however the permission to go on. Alternatively (to blocking send or async+callback), you could standby somwhere, waiting for a green light - that would equal using a fork here. A fork could be useful, provided it won't block. If you are in a loop, you could wait for the forked variable (from APLSocket.⎕SendAsync) to get a value, and once it has it, you could continue with the next cycle. But then the loop must be non-blocking for thread 0 from start. You cannot allow the fork to block thread 0. Same, if the APLSocket.⎕SendAsync happened in an isolate; you cannot allow the fork to block. (IF the entire send logic (the "full thing") resides in an isolate, then it might be highly interesting and efficient, but that's another story).

Remember: If there is relevant blocking somewhere, you can as well do a fallback to blocking-send-in-the-first-place.

2. Receive
You must be listening. There is no way to avoid that, and you can have *no* expectations regarding when a packet arrives. It *may* arrive close after something you sent, but that is not at all guaranteed, nor necessarily likely. "Close to" may be delayed, the other end may of one or another reason not reply, or maybe you simply didn't ask for a reply in the first place, or maybe it's the other end who took the initiative.

Eg. in a multiplayer game environment, you do not Ack each UDP package. That would take way too much time. You may once in a while ask the other party if he's still alive, but in between you may send 500 packets with the simple assumption that he is getting them. Another thing to note is that old UDP packets have no market value. They are replaced with newer ones quickly and only the newest one is interesting.

Why use a fork when receiving? Yes, you could let the fork "initiate" the listening. Call some APLSocket.⎕ReceiveAsync but write it's result to a fork, then immediately after (or even at the same row) query the value of the fork - and you are listening/hanging. But again, this may not cause thread 0 blocking, so the function we reside in must be a separate APL thread.

- - -

Using a forks would simplify the code for both async send and receive if APL had ⎕ASYNC-METHODS for those. But i doubt a fork by itself would speed up anything, compared to a ⎕ASYNC-METHOD + callbackfunction solution.

Isolates for just the individual send's and receive's would imho neither speed up anything. The OS socket stack is inherently multi-threaded, so APL could only affect the transmission speed between APL memory manager and OS socket buffers. Is there any reason to use isolates for that? An isolate would in any case involve an equal (or bigger) amount of data transmission. While, if placing a larger set of send/receive logic&hosekeeping in an isolate, then it would most certainly be beneficial. But as said that's another story.

I'd like to bring up 3 companion issues:

1. One of the biggest problems with UDP is packet composing. Packets are bytes and usually have fixed data locations, or then the packet holds headers with data lengths etc. Having flexible data lengths takes time, so fixed fields are more efficient. Request:

I'd really, really, really encourage you to allow user-defined, user-declared hard typing. This would ease packet composing/improve composing performance considerably. Meaning, consider that i want to transmit the result of an equation, variable A, potentially a 8-byte float, so i have 8 bytes reserved in the UDP packet. But if A happens to get the value 1, well you know the rest... If i was allowed to hard-type A into float64, always, then i could probably easily slot it in the UDP packet as 8 bytes with no conversion issues!

This would have another advantage as well:

2. Compiler: One of the big problems with the compiler seems to be data type and shape. You cannot compile as APL is flexible re. type and shape. I have been ranting about this for years now :-). Please allow me to tell the compiler/interpreter what it needs to know about variable types and shapes. Let me declare this somewhere! My declarations will hold for the lifetime of the variable, i promise. Go by that, compile by that, let me resolve the value 1 from bytecode as 8 bytes, ready for the UDP packet! Crash if i was wrong..

3. Persistance. The less new work, the higher performance. Also called for example cache. APL ⎕UDPstuffAsync logic should split up whatever needs to be split, so that not each transmission will re-create things. Hence, perhaps let IP endpoints be separate objects, and if there is anything else that is likely to repeat, or repeat with only a small change/adjustment, let that be a separate thingy!.

/ More than 7 cents
Tomas Gustafsson
 
Posts: 91
Joined: Mon Sep 19, 2011 6:43 pm

Re: How to use Async and Await ?

Postby Morten|Dyalog on Thu Jan 14, 2016 8:09 am

Tomas Gustafsson wrote:More that 7 cents!

You can say that again!
Tomas Gustafsson wrote:I'd really wish you enlarged the UDP ⎕-functions for async behaviour instead

This is a little confusing - which ⎕-functions are you referring to?
Tomas Gustafsson wrote:The ws is kind of a holy thing, and invoking an additional, large set of code (Conga), just to get async transmission doesn't seem like the optimal way to go. At least not if you manage the transmission yourself.

Well, the whole point of Conga is that *it* manages the transmission and the listening in a multi-threaded C DLL outside the workspace. The Conga workspace *is* a little bit large, but you don't need all of it - we will make it significantly lighter in version 15.0. Invoking the .NET framework can hardly be called a "lightweight" solution!
Tomas Gustafsson wrote:UDP send and receive are inherently two different things! They have nothing to do with each other.

If Conga supported UDP, that is what you would get: A function to send, and Conga would permanently be listening in a C thread and just tell you when something arrived. That is how I imagine it, anyway. You would not need to fork anything, as Conga is permanently running in a separate thread from you anyway. Your threads remain permanently free, and you could throw away a ton of code that you currently have to jump through hoops backwards to use .NET Asynch features etc.
Tomas Gustafsson wrote:Isolates for just the individual send's and receive's would imho neither speed up anything.

Agreed, Isolates are too heavyweight for this. What I was suggesting is that we could add a "promise" as a new type of array, something that could be filled in by a thread which was waiting for Conga to say "Got a message for you", and then gave the "promise" a value. Meanwhile, a thread which decided it NEEDED the value could just refer to it and would automatically block on the promise until the value arrived. At the moment, the only way to manufacture a promise is to invoke a function call to an isolate, but we could allow application code to create promises when needed, as a very easy way to wait for asynchronous calls.
Tomas Gustafsson wrote:I'd really, really, really encourage you to allow user-defined, user-declared hard typing. This would ease packet composing/improve composing performance considerably.

Noted (several times), and I am sure we'll get there eventually. But it would be a mistake to wait for this. APL already allows you to have fixed size arrays: Just create them and then only modify them using modified assignments. I can't believe that this would have a significant impact on your application performance.
User avatar
Morten|Dyalog
 
Posts: 315
Joined: Tue Sep 09, 2008 3:52 pm

Previous

Return to Microsoft.NET

Who is online

Users browsing this forum: No registered users and 1 guest