Today we talked about the CPS variants as closure callbacks (typically C # and JS), and Lua, a coroutine with a real stack that can yield and resume, the pros and cons of two solutions for writing asynchronous processing logic in a synchronous manner. It suddenly occurred to me that both of these practices would be more expensive. My own judgment is that the closure callback will perform better in a single call with only one or two interrupts (i.e. 2 callbacks, or 2 yield), because the Coroutine method needs to create a co-process with a full stack, which is relatively heavy. But if there is a lot of asynchronous calls in a single call, then the coroutine is better, because no matter how many times yield,coroutine always only need to create a co-process, and every call to the closure callback must create a closure function, the GC's overhead is not small. Directly on the test code
Cps:
LocalCount =1000000LocalList1 = {}LocalList2 = {}LocalClock =Os.clockLocalInsert =Table.insertLocalremove =Table.removeLocal functionSETCB (FN) insert (List1, FN)EndLocal functiontest1 () SETCB (function() End)EndLocaltime1 = Clock ()--Start fori =1, Count Dotest1 ()EndLocalTime2 = Clock ()--called while true DoList1, List2=List2, List1 fori =1, #list2 DoRemove (List2) ()End if#list1 = =0 Then Break EndEndLocalTime3 = Clock ()--callback completely endedPrint(Time2-time1, Time3-time2)
Coroutine:
LocalCount =1000000LocalList1 = {}LocalList2 = {}LocalClock =Os.clockLocalInsert =Table.insertLocalremove =Table.removeLocalCreate =coroutine.createLocalYield =Coroutine.yieldLocalrunning =coroutine.runningLocalResume =Coroutine.resumeLocal functionSETCB () Insert (List1, running ()) yield ()EndLocal functiontest2 () SETCB ( )EndLocal functiontest1 () Resume (Create (test2))EndLocaltime1 = Clock ()--Start fori =1, Count Dotest1 ()EndLocalTime2 = Clock ()--called while true DoList1, List2=List2, List1 fori =1, #list2 DoResume (remove (LIST2))End if#list1 = =0 Then Break EndEndLocalTime3 = Clock ()--callback completely endedPrint(Time2-time1, Time3-time2)
Output:
Coroutine calls and wake/callbacks are much slower than closure callbacks
(PS. Here's an episode where I set the Count = 10000000 before, but test the coroutine times for out-of-memory errors, so you can only drop an order of magnitude to test)
Next I'm going to increase the number of callbacks for a single call.
Cps:
LocalCount =1000000LocalList1 = {}LocalList2 = {}LocalClock =Os.clockLocalInsert =Table.insertLocalremove =Table.removeLocal functionSETCB (FN) insert (List1, FN)EndLocal functiontest1 () SETCB (function() SETCB (function() SETCB (function() SETCB (function() SETCB (function() SETCB (function() SETCB (function() End) End) End) End) End) End) End)EndLocaltime1 = Clock ()--Start fori =1, Count Dotest1 ()EndLocalTime2 = Clock ()--called while true DoList1, List2=List2, List1 fori =1, #list2 DoRemove (List2) ()End if#list1 = =0 Then Break EndEndLocalTime3 = Clock ()--callback completely endedPrint(Time2-time1, Time3-time2)
Coroutine:
LocalCount =1000000LocalList1 = {}LocalList2 = {}LocalClock =Os.clockLocalInsert =Table.insertLocalremove =Table.removeLocalCreate =coroutine.createLocalYield =Coroutine.yieldLocalrunning =coroutine.runningLocalResume =Coroutine.resumeLocal functionSETCB () Insert (List1, running ()) yield ()EndLocal functiontest2 () SETCB () SETCB () SETCB () SETCB () SETCB () SETCB () SETCB ()EndLocal functiontest1 () Resume (Create (test2))EndLocaltime1 = Clock ()--Start fori =1, Count Dotest1 ()EndLocalTime2 = Clock ()--called while true DoList1, List2=List2, List1 fori =1, #list2 DoResume (remove (LIST2))End if#list1 = =0 Then Break EndEndLocalTime3 = Clock ()--callback completely endedPrint(Time2-time1, Time3-time2)
Output:
The cost of the callback is still coroutine at a disadvantage, but it's already closer. Startup consumption, because coroutine need to create a larger stack, relative to the closure or relatively heavy, so the start is still much slower than the way the closure callback.
Finally, I changed the number of asynchronous interface calls in one call to 98 times (and then more LUA would error: Chunk has too many syntax levels), compared to the following (this is changed to Count = 100000):
Conclusion: Closures still have advantages.
[Original] Two models of asynchronous programming, the closure callback, and the coroutine of Lua, in which case it consumes much more