
I had an idea based on the Trios implementation. Trying to move the load method implementation out of the loop scope but avoiding the subclassing. My primary concern with the instrumentation was the performance impact. I've run some tests in one experimental branch, instrumenting 4 events for the _run_once function. The idea was to see how the performance degrades, despite there were not listeners/instruments attached. My gut feelings said that only the call to emit the event and the proper check to see if there were listeners will have an expensive performance cost. The numbers were pretty clear, the default Asyncio loses a 10% of performance. Degrading from the 30K coroutines per second to 27K in my laptop. Therefore, looks like we are facing a stopper that would need to be addressed first. How might Python address this situation? Basically implementing a new object type that will provide the same interface as a Signal. Many languages have their own implementation C# [1], Java [2] or they have libraries that support Signals or Events. Indeed Python has plenty of them implemented as independent modules, the most known are Django Signals [3] or Blinker [4]
From my understanding here the problem is the language by itself, having the interpreter behind the scenes.
What I would propose here is a new Python object called Signal implemented as a new type in the CPython API that will allow the developers to implement the following snippet having the chance to speed up the code or at least don't be penalized when there are no observers listening from a specific event. class Foo: def __init__(): self._cb = [] def add_listener(self, cb): self._cp.append(cb) def _signal(self, msg): for cb in self._cb: cb(msg) def run(): self._signal("start") self._signal("stop") f = Foo() f.run() def print_signal(msg): print(msg) f.add_listener(print_signal) f.run() This previous snippet might be implemented using a new object type called signal, having the next snippet: class Foo: def __init__(): self.start = signal() self.stop = signal() def run(): self.start.send() self.stop.send() f = Foo() f.run() def start(): print(msg) def stop(): print(msg) f.start.connect(start) f.stop.connect(stop) f.run() This kind of pattern is spread in many frameworks [5], and they might get benefited for free thanks to this new type of object. I'm wondering if there were other attempts in the past to implement this kind of object in the CPython API. There is some resistance to modify or add superfluous code in the code if it can be implemented as a Python module? The simple fact to become a must for an internal feature might enough to think on that? Thoughts ? [1] http://docs.oracle.com/javase/tutorial/uiswing/events/index.html [2] https://msdn.microsoft.com/en-us/library/aa645739(v=vs.71).aspx [3] https://docs.djangoproject.com/en/1.11/topics/signals/ [4] http://pythonhosted.org/blinker/ [5] http://flask.pocoo.org/docs/0.12/signals/ On Tue, Aug 22, 2017 at 2:36 PM, Pau Freixes <pfreixes@gmail.com> wrote:
One approach would be to add a generic instrumentation API. Trio has something like this, that I think is expressive enough to let Pau implement their busyness checking as a library:
https://trio.readthedocs.io/en/latest/reference-core.html#instrument-api
This has several advantages over subclassing: multiple libraries can define their own instrumentation without interfering with each other, you don't have to redefine the instrumentation for every loop implementation, and you don't have to hook up the instrumentation when setting up the loop, e.g. you could just do something like:
import toobusy with toobusy.Monitor(loop) as monitor: if monitor.toobusy(): ...
It will help also other loops to meet the same contract making them compatibles with already implemented instruments. Maybe the major concern here is the performance penalty, do you have some numbers about how negligible is have all of these signals available to be used? -- --pau
-- --pau