Thank you Jonathan and Andrew, it's more clear to me. I'm digging with time counters in source code to try to find a good compromise.
Regards. -- Ludovic Gasc On Thu, Jan 29, 2015 at 5:10 PM, Andrew Svetlov <[email protected]> wrote: > Task creation requires one extra event loop iteration to start. > > On Thu, Jan 29, 2015 at 6:03 PM, Jonathan Slenders > <[email protected]> wrote: > > 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. > >> > >> > > > > > > -- > Thanks, > Andrew Svetlov >
