{"id":19137598,"url":"https://github.com/archibate/ezprof","last_synced_at":"2026-06-24T07:31:16.683Z","repository":{"id":92121228,"uuid":"289822778","full_name":"archibate/ezprof","owner":"archibate","description":"Easy and intuitive Python profiling API","archived":false,"fork":false,"pushed_at":"2020-08-24T04:06:27.000Z","size":5,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-13T07:21:51.670Z","etag":null,"topics":["profiling","python","timing"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/archibate.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-24T03:54:01.000Z","updated_at":"2021-01-02T10:15:56.000Z","dependencies_parsed_at":"2023-03-17T09:01:03.051Z","dependency_job_id":null,"html_url":"https://github.com/archibate/ezprof","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/archibate/ezprof","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archibate%2Fezprof","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archibate%2Fezprof/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archibate%2Fezprof/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archibate%2Fezprof/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/archibate","download_url":"https://codeload.github.com/archibate/ezprof/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archibate%2Fezprof/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34722619,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-24T02:00:07.484Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["profiling","python","timing"],"created_at":"2024-11-09T06:38:54.925Z","updated_at":"2026-06-24T07:31:16.650Z","avatar_url":"https://github.com/archibate.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"EzProf\n======\n\nEasy and intuitive Python profiling API, for example:\n\n.. code-block:: python\n\n    import ezprof\n    import time\n\n\n    @ezprof.timed\n    def func():\n        time.sleep(0.0233)\n\n    func()\n\n    with ezprof.scope('hello'):\n        time.sleep(0.2)\n\n    ezprof.start('world')\n    for i in range(450):\n        pass\n    ezprof.stop('world')\n\n    ezprof.show()\n\n\nBasically it measures time spent between ``start()`` and ``stop()`` using\nthe Python-builtin function ``time.time()``.\n\nProfiling APIs\n**************\n\nThere are 3 ways to use this profiler:\n\n1. ``ezprof.start()`` and ``ezprof.stop()`` are the most fundemental APIs:\n   It will measure the time difference between ``start`` and ``stop``.\n   Then save the result according to the given name. e.g.:\n\n.. code-block:: python\n\n    import ezprof\n    import time\n\n    def do_something_A():\n        time.sleep(0.01)\n\n    def do_something_B():\n        time.sleep(0.1)\n\n    ezprof.start('A')\n    do_something_A()\n    ezprof.stop('A')\n\n    ezprof.start('B')\n    do_something_B()\n    ezprof.stop('B')\n\n    ezprof.show()\n\n\n.. code-block:: none\n\n      min   |   avg   |   max   |  num  |  total  |    name\n     0.100s |  0.100s |  0.100s |    1x |  0.100s | B\n    10.10ms | 10.10ms | 10.10ms |    1x | 10.10ms | A\n\n\n2. ``with ezprof.scope()``, this one makes our code cleaner and readable.\n   Basically, it will automatically invoke ``stop`` when the indented block exited.\n   For more details about the ``with`` syntax in Python, see `this tutorial \u003chttps://www.pythonforbeginners.com/files/with-statement-in-python\u003e`_.\n\n.. code-block:: python\n\n    import ezprof\n    import time\n\n    def do_something_A():\n        time.sleep(0.01)\n\n    def do_something_B():\n        time.sleep(0.1)\n\n    with ezprof.scope('A'):\n        do_something_A()\n    # automatically invoke stop('A')\n\n    with ezprof.scope('B'):\n        do_something_B()\n    # automatically invoke stop('B')\n\n    ezprof.show()\n\n\n.. code-block:: none\n\n      min   |   avg   |   max   |  num  |  total  |    name\n     0.100s |  0.100s |  0.100s |    1x |  0.100s | B\n    10.10ms | 10.10ms | 10.10ms |    1x | 10.10ms | A\n\n\n3. ``@ezprof.timed``, this one is very intuitive when profiling functions.\n   It will measure the time spent in the function, i.e. ``start`` when entering the function, ``stop`` when leaving the function, and the record name is the function name.\n\n.. code-block:: python\n\n    import ezprof\n    import time\n\n    @ezprof.timed\n    def do_something_A():\n        time.sleep(0.01)\n\n    @ezprof.timed\n    def do_something_B():\n        time.sleep(0.1)\n\n    do_something_A()\n    do_something_B()\n\n    ezprof.print()\n\n\n.. code-block:: none\n\n      min   |   avg   |   max   |  num  |  total  |    name\n     0.100s |  0.100s |  0.100s |    1x |  0.100s | do_something_B\n    10.10ms | 10.10ms | 10.10ms |    1x | 10.10ms | do_something_A\n\n\n.. warning::\n\n    When combining ``@ezprof.timed`` with other decorators, then\n    ``@ezprof.timed`` should be put **above** it to get desired output, e.g.:\n\n    .. code-block:: python\n\n        def my_decorator(foo):\n            def wrapped():\n                do_something()\n                return foo()\n\n        @ezprof.timed\n        @my_decorator\n        def substep():\n            ...\n\n\nRecording multiple entries\n**************************\n\nWhen a same **name** is used for multiple times, then they will be merged into one, e.g.:\n\n.. code-block:: python\n\n    import ezprof\n    import time\n\n    def do_something_A():\n        time.sleep(0.01)\n\n    def do_something_B():\n        time.sleep(0.1)\n\n    ezprof.start('A')\n    do_something_A()\n    ezprof.stop('A')\n\n    ezprof.start('A')\n    do_something_B()\n    ezprof.stop('A')\n\n    ezprof.start('B')\n    do_something_B()\n    ezprof.stop('B')\n\n    ezprof.show()\n\nwill obtain:\n\n.. code-block:: none\n\n      min   |   avg   |   max   |  num  |  total  |    name\n    10.10ms | 55.12ms |  0.100s |    2x |  0.110s | A\n     0.100s |  0.100s |  0.100s |    1x |  0.100s | B\n\n\n- ``min`` is the minimum time in records.\n- ``avg`` is the average time of records.\n- ``max`` is the maximum time in records.\n- ``num`` is the number of record entries.\n- ``total`` is the total costed time of records.\n\n\nProfiler options\n****************\n\nDue to Taichi's JIT mechanism, a kernel will be **compiled** on its first invocation.\nSo the first record will be extremely long compared to the following records since it\n**involves both compile time and execution time**, e.g.:\n\n.. code-block:: none\n\n       min   |   avg   |   max   |  num  |  total  |    name\n      2.37ms |  3.79ms |  1.615s | 1900x |  7.204s | substep\n\n.. code-block:: none\n\n       min   |   avg   |   max   |  num  |  total  |    name\n      2.37ms |  2.95ms | 12.70ms | 1895x |  5.592s | substep\n\n\nAs you see, this make our result inaccurate, especially the ``max`` column.\n\nTo avoid this, you may specify a ``warmup`` option to ``ti.profiler``, e.g.:\n\n.. code-block:: python\n\n    @ezprof.timed(warmup=5)\n    @ti.kernel\n    def substep():\n        ...\n\n\nSet ``warmup=5`` for example, will **discard** the first 5 record entries.\nI.e. discard the kernel compile time and possible TLB and cache misses on start up.\n\n\n.. warning::\n\n    ``ezprof`` **only works in Python-scope** for Taichi users, e.g.:\n\n    .. code-block:: python\n\n        @ti.func\n        def substep():\n            ezprof.start('hello')  # won't work as you expected...\n            ...\n            ezprof.stop('hello')\n\n        @ezprof.timed  # won't work as you expected...\n        @ti.func\n        def hello():\n            ...\n\n        @ezprof.timed  # Kernels are OK!\n        @ti.kernel\n        def hello():\n            ...\n\n    To do profiling **inside Taichi-scope**, please see the ``KernelProfiler``\n    provided by Taichi itself.\n\n(TODO: clarify the relationship between ``ezprof`` and ``taichi#1493``)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farchibate%2Fezprof","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farchibate%2Fezprof","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farchibate%2Fezprof/lists"}