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. SendWindows 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. ReceiveYou
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