Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Prettier Parallel logs if the number of tasks is known - #1608

Merged
tomMoral merged 13 commits into
joblib:mainfrom
altaris:prettier-print
Jan 17, 2025
Merged

Prettier Parallel logs if the number of tasks is known#1608
tomMoral merged 13 commits into
joblib:mainfrom
altaris:prettier-print

Conversation

@altaris

@altaris altaris commented Aug 21, 2024

Copy link
Copy Markdown
Contributor

This PR makes the execution logs of Parallel.print_progress more aligned if the number of tasks is known. An example is worth 1e3 words:

from time import sleep
from joblib.parallel import Parallel, delayed

def task(x):
    sleep(.1)
    return x * x

Parallel(n_jobs=32, verbose=1)([delayed(task)(i) for i in range(10000)])

Before:

[Parallel(n_jobs=32)]: Using backend LokyBackend with 32 concurrent workers.
[Parallel(n_jobs=32)]: Done 208 tasks      | elapsed:    0.9s
[Parallel(n_jobs=32)]: Done 708 tasks      | elapsed:    2.5s
[Parallel(n_jobs=32)]: Done 1408 tasks      | elapsed:    4.6s
[Parallel(n_jobs=32)]: Done 2308 tasks      | elapsed:    7.6s
[Parallel(n_jobs=32)]: Done 3408 tasks      | elapsed:   11.0s
[Parallel(n_jobs=32)]: Done 4708 tasks      | elapsed:   15.1s
[Parallel(n_jobs=32)]: Done 6208 tasks      | elapsed:   19.8s
[Parallel(n_jobs=32)]: Done 7908 tasks      | elapsed:   25.2s
[Parallel(n_jobs=32)]: Done 9808 tasks      | elapsed:   31.2s
[Parallel(n_jobs=32)]: Done 10000 out of 10000 | elapsed:   31.7s finished

After:

[Parallel(n_jobs=32)]: Using backend LokyBackend with 32 concurrent workers.
[Parallel(n_jobs=32)]: Done   176 tasks        | elapsed:    0.8s
[Parallel(n_jobs=32)]: Done   676 tasks        | elapsed:    2.4s
[Parallel(n_jobs=32)]: Done  1376 tasks        | elapsed:    4.5s
[Parallel(n_jobs=32)]: Done  2276 tasks        | elapsed:    7.5s
[Parallel(n_jobs=32)]: Done  3376 tasks        | elapsed:   10.9s
[Parallel(n_jobs=32)]: Done  4676 tasks        | elapsed:   15.0s
[Parallel(n_jobs=32)]: Done  6176 tasks        | elapsed:   19.7s
[Parallel(n_jobs=32)]: Done  7876 tasks        | elapsed:   25.1s
[Parallel(n_jobs=32)]: Done  9776 tasks        | elapsed:   31.1s
[Parallel(n_jobs=32)]: Done  9937 out of 10000 | elapsed:   31.5s remaining:    0.2s
[Parallel(n_jobs=32)]: Done 10000 out of 10000 | elapsed:   31.7s finished

Note that if the jobs are submitted as a generator instead of a list or tuple

Parallel(n_jobs=32, verbose=1)(delayed(task)(i) for i in range(10000))

then the logs are the same as before. In other words, the generator is not "unrolled" just to know the number of tasks.

@codecov

codecov Bot commented Aug 21, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 95.76%. Comparing base (e02400a) to head (e5033ce).
Report is 46 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1608      +/-   ##
==========================================
+ Coverage   95.24%   95.76%   +0.52%     
==========================================
  Files          45       46       +1     
  Lines        7719     7799      +80     
==========================================
+ Hits         7352     7469     +117     
+ Misses        367      330      -37     
Flag Coverage Δ
?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tomMoral tomMoral left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one question, why not directly display Done XXX out of YYY when it is known?

Comment thread joblib/parallel.py Outdated
Comment thread joblib/parallel.py Outdated
Suggestion from tomMoral

Co-authored-by: Thomas Moreau <[email protected]>
@altaris

altaris commented Nov 13, 2024

Copy link
Copy Markdown
Contributor Author

Just one question, why not directly display Done XXX out of YYY when it is known?

Huh that's a good point 😅

@tomMoral tomMoral left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a last comment but otherwise, good to go for me :)
Thanks for the PR!

Comment thread joblib/parallel.py Outdated
@GaelVaroquaux

GaelVaroquaux commented Nov 13, 2024 via email

Copy link
Copy Markdown
Member

@tomMoral

Copy link
Copy Markdown
Contributor

Yes I agree that using a list is not the pattern we want to promote.

But there are some instances where we can implement both lazy inputs and a len, so why not improve the reporting for those?
For instance, using tqdm to wrap an iterator gives:

from tqdm import tqdm
it = tqdm(range(100), total=100)
len(it)  # 100

while not materializing the tasks. The same is possible with torch.Dataset objects.
So I am in favor of making this small change to improve the output, while still not advocating for using list of tasks.

@GaelVaroquaux

GaelVaroquaux commented Nov 13, 2024 via email

Copy link
Copy Markdown
Member

@tomMoral tomMoral left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A last nitpick. Also, if you could add a test and an entry in the change log , that would be great.
Once again, thanks for the contrib!

Comment thread joblib/parallel.py Outdated
Comment thread joblib/parallel.py Outdated
@altaris

altaris commented Nov 14, 2024

Copy link
Copy Markdown
Contributor Author

A last nitpick. Also, if you could add a test and an entry in the change log , that would be great. Once again, thanks for the contrib!

Where should I add an entry in the changelog?

@altaris

altaris commented Nov 14, 2024

Copy link
Copy Markdown
Contributor Author

A last nitpick. Also, if you could add a test and an entry in the change log , that would be great. Once again, thanks for the contrib!

And I'm not sure how integrate the test in testing.py correctly. For the test logic itself, I'm thinking of something like

vert_pos = {}
for line in PROCESS_OUTPUT_LINES:
    if "|" in line:
        vert_pos.add(line.find("|"))
assert len(vert_pos) in [0, 1]

to make sure the | always appears at the same position.

@tomMoral

Copy link
Copy Markdown
Contributor
  • For the changelog: https://github.com/joblib/joblib/blob/main/CHANGES.rst
  • We are relying on pytest for the tests, so adding a function with a name test_* in the joblib/test/test_parallel.py file will add a new test. To check the displayed text after a call to Parallel, you can add capsys as an argument to the new test entry and take inspiration from this test.

Note that if you are not familiar with pytest, I can add a test by myself so feel free to let me know.

@altaris

altaris commented Nov 16, 2024

Copy link
Copy Markdown
Contributor Author

A last nitpick. Also, if you could add a test and an entry in the change log , that would be great. Once again, thanks for the contrib!

@tomMoral tomMoral closed this Jan 17, 2025
@tomMoral tomMoral reopened this Jan 17, 2025

@tomMoral tomMoral left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! I did a few change to integrate the test better with the test_parallel.py but otherwise it is good to go!
Thanks a lot :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants