>From my experience, Task creation can result a noticeable overhead when you 
try to split up work in many *really* small tasks. "Yield from" is really 
light, but when you create a Task, you create an instance of such a Python 
object and "yield from task" will actually proxy through Task.__iter__ (I 
think).

So, performance-wise, I think you should only create tasks for a coroutines 
which spend a certain amount of their time waiting for I/O and you want to 
fill these "I/O" gaps with other tasks.
But in a web server, where you have many requests, there's a chance that 
these gaps are already filled by a parallel requests anyway. So, creating 
more tasks could reduce latency when the load is low, but increase the 
latency (because of CPU saturation) when the load is high.

I hope this explains. (And please correct me if I'm wrong.)

About the last example, I'm not sure.



Le dimanche 25 janvier 2015 00:07:41 UTC+1, Ludovic Gasc a écrit :
>
> Hi,
>
> I've a "strange" behaviour with AsyncIO: more I try to improve the 
> performances, more it's slow.
> I certainly missed something in AsyncIO, or maybe you have some tips other 
> than try and benchmark for each change.
>
> For example, this is two coroutines to update data from PostgreSQL, 
> executed by aiohttp.web and API-Hour:
> (If you want to see all code, it's available here:
>
> https://github.com/Eyepea/FrameworkBenchmarks/tree/API-Hour/frameworks/Python/API-Hour/hello/hello
>  
> )
>
> @asyncio.coroutine
> def update_random_records(container, limit):
>     results = []
>     for i in range(limit):
>         results.append((yield from update_random_record(container)))
>
>     return results
>
>
> @asyncio.coroutine
> def update_random_record(container):
>     pg = yield from container.engines['pg']
>
>     world = yield from get_random_record(container)
>
>     with (yield from pg.cursor()) as cur:
>         yield from cur.execute('UPDATE world SET 
> randomnumber=%(random_number)s WHERE id=%(idx)s',
>                                {'random_number': randint(1, 10000), 'idx': 
> world['Id']})
>     return world
>
>
> When I launch wrk (HTTP benchmark tool) on HTTP server, I've this result:
>
>
> lg@steroids:~$ wrk -t8 -c256 -d30s http://127.0.0.1:8008/updates?queries=20
>
> Running 30s test @ http://127.0.0.1:8008/updates?queries=20 8 threads and 
> 256 connections Thread Stats Avg Stdev Max +/- Stdev Latency 547.86ms 
> 428.03ms 2.90s 85.37% Req/Sec 59.53 12.62 92.00 69.32% 14283 requests in 
> 30.04s, 10.99MB read Socket errors: connect 0, read 0, write 0, timeout 37 
> Requests/sec: 475.42 
>
> Transfer/sec:    374.46KB
>
>
> Now, when I try to change coroutine update_random_records to launch all 
> update_random_record coroutines in the same time instead to wait the end of 
> update_random_record to launch a new coroutine:
>
>
> @asyncio.coroutine
> def update_random_records(container, limit):
>     tasks = []
>     results = []
>     for i in range(limit):
>         
> tasks.append(container.loop.create_task(update_random_record(container)))
>     yield from asyncio.wait(tasks)
>     for task in tasks:
>         results.append(task.result())
>     return results
>
>
> I've this result:
>
>
> lg@steroids:~$ wrk -t8 -c256 -d30s http://127.0.0.1:8008/updates?queries=20
> Running 30s test @ http://127.0.0.1:8008/updates?queries=20
>   8 threads and 256 connections
>   Thread Stats   Avg      Stdev     Max   +/- Stdev
>     Latency   585.21ms  563.88ms   3.95s    89.03%
>     Req/Sec    57.56     18.82   118.00     66.89%
>   13480 requests in 30.04s, 10.37MB read
>   Socket errors: connect 0, read 0, write 0, timeout 193
> Requests/sec:    448.76
> Transfer/sec:    353.49KB
>
>
> As you can see, less requests/sec but also more HTTP requests in timeout. The 
> limitation should be my PostgreSQL database.
>
> And now, if I add a Semaphore(value=10) to reduce concurrent coroutines of 
> update_random_records :
>
>
> @asyncio.coroutine
> def update_random_record(container):
>     with (yield from container.semaphores['updates']):
>         pg = yield from container.engines['pg']
>
>         world = yield from get_random_record(container)
>
>         with (yield from pg.cursor()) as cur:
>             yield from cur.execute('UPDATE world SET 
> randomnumber=%(random_number)s WHERE id=%(idx)s',
>                                    {'random_number': randint(1, 10000), 
> 'idx': world['Id']})
>         return world
>
>
> Now:
>
> lg@steroids:~$ wrk -t8 -c256 -d30s http://127.0.0.1:8008/updates?queries=20
>
> Running 30s test @ http://127.0.0.1:8008/updates?queries=20 8 threads and 
> 256 connections Thread Stats Avg Stdev Max +/- Stdev Latency 619.24ms 
> 476.83ms 3.20s 81.59% Req/Sec 52.74 9.49 81.00 69.92% 12590 requests in 
> 30.03s, 9.68MB read Socket errors: connect 0, read 0, write 0, timeout 53 
> Requests/sec: 419.23 Transfer/sec: 330.21KB 
>
>
> It's better, but less rapid than the first attempt with a simple yield form 
> in a loop. Change Semaphore value doesn't change the result a lot.
>
> I'd already have this problem with AsyncIO in another context, slurp all 
> databases content of a CouchDB instance: When I've tried to launch several 
> I/O HTTP requests in same time, Python script had needed more time.
>
>
> It could be the context switching between coroutines that reduces performance 
> ? How to identify/measure that ?
>
>
> Thanks for your ideas.
>
>
> Regards.
>
>
>

Reply via email to