Python异步协同例程执行时间

时间:2017-09-01 16:47:00

标签: python-3.x async-await python-asyncio measurement aiohttp

我试图测量python异步函数的执行时间。但是我遇到了问题,因为函数启动时的实际点是未知的。

测量功能代码:

def timer(func):
    async def process(func, *args, **params):
        if asyncio.iscoroutinefunction(func):
            print('this function is a coroutine: {}'.format(func.__name__))
            return await func(*args, **params)
        else:
            print('this is not a coroutine')
            return func(*args, **params)

    async def helper(*args, **params):
        print('{}.time'.format(func.__name__))
        start = datetime.datetime.now()
        print(start)

        result = await process(func, *args, **params)

        finish = datetime.datetime.now()
        print('>>> calculated - ', finish - start, 'start-', start, 'finish-', finish)
        return result

    return helper

休息代码:

@timer
async def fetch(name, session):
    url = 'http://localhost:8808/'
    payload = {}

    async with session.put(url, headers=HEADERS, json=payload) as response:
        session.get_connection_count()
        response_data = await response.read()
        result = {'response_code': response.status,
              'response_data': response_data}
    return result


def on_data_received(future):
    # pass
    response_obj = json.loads(future.result()['response_data'])
    response_code = future.result()['response_code']
    device_id = response_obj.get('clientDeviceID')

async def run(r):
    connector = DecoratedTCPConnector(limit_per_host=10000, limit=20000)

    with TimedClientSession(connector=connector) as client:
        for i in range(r):
            id = ''.join([random.choice('01234') for x in range(16)])

            task = asyncio.ensure_future(fetch(id, client))
            task.add_done_callback(on_data_received)
            tasks.append(task)
        return await asyncio.gather(*tasks)

开始时间实际上是将例程任务添加到队列的时间,但我需要发送PUT请求的时间。

欢迎任何建议。

1 个答案:

答案 0 :(得分:2)

在asyncio网络中进行精确的延迟测量几乎是不可能的(AFAIK,很想成为错误)。

问题在create_connection的文档中暗示:

  

...此方法是一个协同程序,它将尝试在后台建立连接...

因为这是aiohttp使用的基本构建块,它是一个协程,你可以礼貌地要求事件循环在它准备就绪时执行,而不是现在直接调用;它不可能获得连接的确切时间,因此请求获取。 (另外,技术上存在一个问题,即你所谓的“发送”时间,是包括建立连接所花费的时间,还是连接后传输数据所花费的时间?你在连接时间中包含名称解析吗?)< / p>

但是,您可以比上面的代码做得更好,例如上面用timer测量的时间将包括等待会话连接池中的连接可用的任何时间。

这里有一些代码可以让你两次:

  • 连接开始的大致时间(计划create_connection协程之前的时间)
  • 和(可能)更准确地确定连接的时间,例如。传输的connection_made被调用的时间。

这些时间都不是完美的,但除非您的事件循环非常繁忙,否则它们可能足够准确,无法进行任何合理的测量。

import asyncio
import functools
from time import time
import aiohttp
from aiohttp.client_proto import ResponseHandler

async def main():
    connection_started_time = None
    connection_made_time = None

    class TimedResponseHandler(ResponseHandler):
        def connection_made(self, transport):
            nonlocal connection_made_time
            connection_made_time = time()
            return super().connection_made(transport)

    class TimedTCPConnector(aiohttp.TCPConnector):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self._factory = functools.partial(TimedResponseHandler, loop=loop)

        async def _create_connection(self, req):
            nonlocal connection_started_time
            connection_started_time = time()
            return await super()._create_connection(req)

    async with aiohttp.ClientSession(connector=TimedTCPConnector(loop=loop)) as session:
        async with session.get('https://www.google.com') as response:
            await response.text()
        print(f'>>> time taken including connection time: {time() - connection_started_time:0.3f}s')
        print(f'>>> time taken once connection made: {time() - connection_made_time:0.3f}s')

loop = asyncio.get_event_loop()
loop.run_until_complete(main())