Skip to content

Consider using mypyc to speed things up like black does #159

@Bobronium

Description

@Bobronium

Black is significantly faster than ufsort, especially when it produces no changes.
I believe such performance difference may be due to black using mypyc: https://github.com/ichard26/black-mypyc-wheels

black is 4.25 times faster on check:

curl -s https://raw.githubusercontent.com/facebookexperimental/usort/main/usort/api.py -o example.py

time black --check example.py

# All done! ✨ 🍰 ✨
# 1 file would be left unchanged.
# black --check example.py  0.08s user 0.02s system 75% cpu 0.137 total

time usort check example.py            
# usort check example.py  0.34s user 0.04s system 86% cpu 0.434 total

black is 2 times faster when making changes:

curl -s https://raw.githubusercontent.com/python/cpython/main/Lib/asyncio/futures.py -o example.py  
                                                                                                                          
time black example.py                                                                             
# reformatted example.py

# All done! ✨ 🍰 ✨
# 1 file reformatted.
# black example.py  0.15s user 0.02s system 75% cpu 0.229 total
                                                                                                                          
time usort format example.py                                                                      
# Sorted example.py
# usort format example.py  0.40s user 0.04s system 93% cpu 0.469 total

However looking at profiler run, I see that 3/5 of the time is taken by trailrunner (and nearly 90% of that time is spend in lock.acquire), despite usort was given only one file path:

pyinstrument --show-all -m usort format example.py
pyinstrument --show-all -m usort format example.py

_     ._   __/__   _ _  _  _ _/_   Recorded: 05:51:13  Samples:  167
/_//_/// /_\ / //_// / //_'/ //     Duration: 0.405     CPU time: 0.153
/   _/                      v4.1.1

Program: usort format example.py

0.404 <module>  <string>:1
└─ 0.404 run_module  runpy.py:199
 ├─ 0.266 _run_module_code  runpy.py:89
 │  └─ 0.266 _run_code  runpy.py:63
 │     └─ 0.266 <module>  usort/__main__.py:1
 │        ├─ 0.256 __call__  click/core.py:1128
 │        │  └─ 0.256 main  click/core.py:987
 │        │     └─ 0.256 invoke  click/core.py:1623
 │        │        └─ 0.256 invoke  click/core.py:1393
 │        │           └─ 0.256 invoke  click/core.py:709
 │        │              └─ 0.256 wrapper  usort/cli.py:37
 │        │                 └─ 0.256 format  usort/cli.py:177
 │        │                    └─ 0.255 usort_path  usort/api.py:120
 │        │                       ├─ 0.242 run  trailrunner/core.py:226
 │        │                       │  └─ 0.242 run  trailrunner/core.py:171
 │        │                       │     ├─ 0.216 _chain_from_iterable_of_lists  concurrent/futures/process.py:561
 │        │                       │     │  └─ 0.216 result_iterator  concurrent/futures/_base.py:601
 │        │                       │     │     └─ 0.216 result  concurrent/futures/_base.py:417
 │        │                       │     │        └─ 0.216 wait  threading.py:288
 │        │                       │     │           └─ 0.216 lock.acquire  <built-in>:0
 │        │                       │     ├─ 0.019 __exit__  concurrent/futures/_base.py:635
 │        │                       │     │  └─ 0.019 shutdown  concurrent/futures/process.py:739
 │        │                       │     │     └─ 0.019 join  threading.py:1057
 │        │                       │     │        └─ 0.019 _wait_for_tstate_lock  threading.py:1095
 │        │                       │     │           └─ 0.019 lock.acquire  <built-in>:0
 │        │                       │     └─ 0.004 _default_executor  trailrunner/core.py:128
 │        │                       │        └─ 0.004 __init__  concurrent/futures/process.py:581
 │        │                       └─ 0.012 walk  trailrunner/core.py:210
 │        │                          └─ 0.012 walk  trailrunner/core.py:136
 │        │                             └─ 0.012 gitignore  trailrunner/core.py:67
 │        │                                └─ 0.011 pathspec  trailrunner/core.py:54
 │        │                                   └─ 0.011 from_lines  pathspec/pathspec.py:100
 │        │                                      └─ 0.011 <listcomp>  pathspec/pathspec.py:126
 │        │                                         └─ 0.011 __init__  pathspec/pattern.py:71
 │        │                                            └─ 0.009 compile  re.py:249
 │        │                                               └─ 0.009 _compile  re.py:288
 │        │                                                  └─ 0.008 compile  sre_compile.py:759
 │        │                                                     └─ 0.006 parse  sre_parse.py:937
 │        │                                                        └─ 0.006 _parse_sub  sre_parse.py:435
 │        │                                                           └─ 0.006 _parse  sre_parse.py:493
 │        └─ 0.010 <module>  usort/cli.py:1
 │           └─ 0.007 <module>  click/__init__.py:1
 │              └─ 0.007 <module>  click/core.py:1
 └─ 0.138 _get_module_details  runpy.py:103
    └─ 0.138 _get_module_details  runpy.py:103
       └─ 0.138 <module>  usort/__init__.py:1
          └─ 0.138 <module>  usort/api.py:1
             ├─ 0.114 <module>  usort/sorting.py:1
             │  ├─ 0.101 <module>  libcst/__init__.py:1
             │  │  ├─ 0.038 <module>  libcst/_nodes/expression.py:1
             │  │  │  ├─ 0.017 wrap  dataclasses.py:1175
             │  │  │  │  └─ 0.017 _process_class  dataclasses.py:882
             │  │  │  │     ├─ 0.006 _frozen_get_del_attr  dataclasses.py:599
             │  │  │  │     │  └─ 0.006 _create_fn  dataclasses.py:412
             │  │  │  │     ├─ 0.004 _init_fn  dataclasses.py:529
             │  │  │  │     │  └─ 0.004 _create_fn  dataclasses.py:412
             │  │  │  │     └─ 0.004 _cmp_fn  dataclasses.py:624
             │  │  │  │        └─ 0.004 _create_fn  dataclasses.py:412
             │  │  │  └─ 0.016 <module>  libcst/_nodes/op.py:1
             │  │  │     └─ 0.011 wrap  dataclasses.py:1175
             │  │  │        └─ 0.010 _process_class  dataclasses.py:882
             │  │  ├─ 0.021 <module>  libcst/_nodes/module.py:1
             │  │  │  └─ 0.020 <module>  libcst/_nodes/statement.py:1
             │  │  │     └─ 0.016 wrap  dataclasses.py:1175
             │  │  │        └─ 0.016 _process_class  dataclasses.py:882
             │  │  │           ├─ 0.005 _init_fn  dataclasses.py:529
             │  │  │           │  └─ 0.004 _create_fn  dataclasses.py:412
             │  │  │           └─ 0.004 _cmp_fn  dataclasses.py:624
             │  │  │              └─ 0.004 _create_fn  dataclasses.py:412
             │  │  ├─ 0.014 <module>  libcst/_parser/entrypoints.py:1
             │  │  │  └─ 0.010 <module>  libcst/_parser/grammar.py:1
             │  │  │     └─ 0.008 <module>  libcst/_parser/conversions/expression.py:1
             │  │  │        └─ 0.006 <module>  libcst/_parser/types/partials.py:1
             │  │  │           └─ 0.006 wrap  dataclasses.py:1175
             │  │  │              └─ 0.006 _process_class  dataclasses.py:882
             │  │  ├─ 0.008 <module>  libcst/metadata/__init__.py:1
             │  │  ├─ 0.007 <module>  libcst/_exceptions.py:1
             │  │  │  └─ 0.004 <module>  libcst/_parser/parso/pgen2/generator.py:1
             │  │  │     └─ 0.004 <module>  libcst/_parser/parso/pgen2/grammar_parser.py:1
             │  │  └─ 0.005 <module>  libcst/_batched_visitor.py:1
             │  └─ 0.012 <module>  usort/translate.py:1
             │     └─ 0.012 <module>  usort/types.py:1
             │        └─ 0.009 <module>  attr/__init__.py:1
             │           └─ 0.005 <module>  attr/converters.py:1
             ├─ 0.016 <module>  trailrunner/__init__.py:1
             │  └─ 0.016 <module>  trailrunner/core.py:1
             │     ├─ 0.005 <module>  multiprocessing/__init__.py:1
             │     │  └─ 0.005 <module>  multiprocessing/context.py:1
             │     │     └─ 0.004 <module>  multiprocessing/reduction.py:1
             │     └─ 0.004 <module>  concurrent/futures/__init__.py:1
             └─ 0.005 <module>  usort/config.py:1

So clearly, there's a lot of room for optimizations even before compiling code with mypyc :)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions