Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove separate default job_kwarg n_jobs for sorters #2712

Merged
merged 8 commits into from May 21, 2024

Conversation

zm711
Copy link
Collaborator

@zm711 zm711 commented Apr 12, 2024

Fixes #2696

Right now we aren't respecting the global n_jobs that user sets so the only way to override use all cores is to specifically pass n_jobs to the sorter. This is unintuitive if we allow the user to set this globally except in the case of sorters where it has to be set locally.

The current global default appears to be n_jobs=1 so we discussed whether that should be changed for people who don't know to set n_jobs so let me know if we want to do that in this PR too.

@zm711 zm711 added sorters Related to sorters module concurrency Related to parallel processing labels Apr 12, 2024
@@ -21,11 +21,11 @@
from .utils import SpikeSortingError, ShellScript


default_job_kwargs = {"n_jobs": -1}
default_job_kwargs = {}
Copy link
Member

Choose a reason for hiding this comment

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

can we just remove this?

in the default_params, we can directly use the get_global_job_kwargs

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We can. Once we decide on the global I get do both changes in a new commit.

Copy link
Member

Choose a reason for hiding this comment

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

@zm711 Let's do this:

  1. We keep n_jobs=1 by default everywhere
  2. We remove default_job_kwargs in sorters
  3. We add a global_job_kwargs_set flag in the core/globals.py to check if the user called the set_global_job_kwargs at least once
  4. in the fix_job_kwargs, if global_job_kwargs_set is False and n_jobs is not in the input, we raise a warning to communicate/encourage to use more jobs

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Cool. I'm in the recording suite for the next couple days. If you are trying to push this with a deadline definitely just close this (or take over this one), otherwise I will do that in a couple days/if one of my experiments ends early.

@alejoe91
Copy link
Member

The current global default appears to be n_jobs=1 so we discussed whether that should be changed for people who don't know to set n_jobs so let me know if we want to do that in this PR too.

I vote for n_jobs=0.8 :)

@samuelgarcia @h-mayorquin @chrishalcrow what do you guys think?

@zm711
Copy link
Collaborator Author

zm711 commented Apr 12, 2024

I guess the one concern with changing to a default of multiprocessing is that we may we see more issues whereas with opt-in not everyone is using those. But if we decide to do a default then I think 0.7 or 0.8 seems safe enough!

@h-mayorquin
Copy link
Collaborator

I think that 0.8 cores sounds good as a default. My intuition is that we should leave one physical core free as most people multitask x D.

@zm711
Copy link
Collaborator Author

zm711 commented Apr 29, 2024

@samuelgarcia now that you're back opinions on n_jobs default? I think we hard code a default for now as we wait for a jobs_kwargs optimizer in the future.

@samuelgarcia
Copy link
Member

If this is for the sorter 0.8 sounds OK (because it is only to write this binary).

But we need to have in mind that on some computing nodes you can have many many cores that would lead to an enormous ram demand.

So for the main n_jobs I prefer to keep 1 so it force the user to think about it to improve speed.

@zm711
Copy link
Collaborator Author

zm711 commented Apr 29, 2024

I don't like changing the n_jobs without the user knowing. So if the desired global n_jobs would be n_jobs=1 then I would prefer just to feed that in to sorters as well and the end user can decide to switch to multiprocessing for sorters. But having n_jobs >1 seems like it might take more debate eh?

@zm711
Copy link
Collaborator Author

zm711 commented May 10, 2024

Based on #2826 and #2820. I think even for sorters we should default to n_jobs=1 because some computers aren't working with multiprocessing at all. Currently not all sorters accept n_jobs as a parameter so maybe we add that to base sorting instead?

@alejoe91
Copy link
Member

I agree. Maybe a warning at the fix_job_kwargs level that "warns" that using n_jobs=1 is not optimal?

@h-mayorquin
Copy link
Collaborator

I am slightly against the warning but I can't think on a better way to let them know : /

Two suggestions if we go with the warning:

  • It should not be in a very general place as some sorters don't accept that and then it becomes and irrelevant warning.
  • There should be a way to deactivate it that does not require you to do multiprocessing. As in, yes, I am aware but no, I don't want to use multiprocesing option.

@alejoe91
Copy link
Member

@samuelgarcia suggested a global flag global_kwargs_set that by session is set to True if the user calls the set_global_job_kwargs. I think it's a good idea :)

@alejoe91
Copy link
Member

@zm711 I made the suggested changes.

Now the fix_job_kwargs warns if n_jobs=1 and it hasn't been set (either globally, or locally).
Also extended a bit the job_kwargs documentation.

@zm711
Copy link
Collaborator Author

zm711 commented May 14, 2024

Thanks Alessio!

Number of jobs to use. With -1 the number of jobs is the same as number of cores
* n_jobs: int | float
Number of jobs to use. With -1 the number of jobs is the same as number of cores.
Using a float between 0 and 1 will use that fraction of the total cores.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm wondering if we should also make clear that technically someone can use n_jobs > number of cores which can actually degrade performance by splitting off too many processes? I didn't actually realize this until I looked at #2817.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you open an issue about this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yep. Will do after I run to a meeting!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done!

@@ -60,7 +60,7 @@


def fix_job_kwargs(runtime_job_kwargs):
from .globals import get_global_job_kwargs
from .globals import get_global_job_kwargs, global_job_kwargs_set
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
from .globals import get_global_job_kwargs, global_job_kwargs_set
from .globals import get_global_job_kwargs, is_set_global_job_kwargs_set

Copy link
Member

Choose a reason for hiding this comment

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

The use of the function better because it ensure that it is a global.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Why? I would love to learn the difference here between checking with the function and just importing the boolean. I don't really use global myself.

Copy link
Member

Choose a reason for hiding this comment

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

I agree with Zach here. It's the same and it's super internal

Copy link
Member

Choose a reason for hiding this comment

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

because it ensure that this import is not masked in between by another variable with the same name.
globals in python are not super garanty unless you put global before.

Copy link
Member

Choose a reason for hiding this comment

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

globals corss moules is evill in python having a function interface is more secure.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good to know. Thanks :)

Copy link
Member

Choose a reason for hiding this comment

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

done in last commit

@@ -99,6 +99,15 @@ def fix_job_kwargs(runtime_job_kwargs):

job_kwargs["n_jobs"] = max(n_jobs, 1)

if "n_jobs" not in runtime_job_kwargs and job_kwargs["n_jobs"] == 1 and not global_job_kwargs_set:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if "n_jobs" not in runtime_job_kwargs and job_kwargs["n_jobs"] == 1 and not global_job_kwargs_set:
if "n_jobs" not in runtime_job_kwargs and job_kwargs["n_jobs"] == 1 and not is_set_global_job_kwargs_set():

Copy link
Member

Choose a reason for hiding this comment

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

done

@samuelgarcia
Copy link
Member

I am OK with the logic. I made a proposal.

Comment on lines 133 to 138
def is_global_job_kwargs_set() -> bool:
"""
Check is the global job kwargs have been manually set.
"""
global global_job_kwargs_set
return global_job_kwargs_set
Copy link
Member

Choose a reason for hiding this comment

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

This was already existing a fiew line afte rthis one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed @samuelgarcia. But I added type hints!

Comment on lines +102 to +110
if "n_jobs" not in runtime_job_kwargs and job_kwargs["n_jobs"] == 1 and not is_set_global_job_kwargs_set():
warnings.warn(
"`n_jobs` is not set so parallel processing is disabled! "
"To speed up computations, it is recommended to set n_jobs either "
"globally (with the `spikeinterface.set_global_job_kwargs()` function) or "
"locally (with the `n_jobs` argument). Use `spikeinterface.set_global_job_kwargs?` "
"for more information about job_kwargs."
)

Copy link
Member

Choose a reason for hiding this comment

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

et voilà.

@samuelgarcia samuelgarcia merged commit 72c7717 into SpikeInterface:main May 21, 2024
11 checks passed
@zm711 zm711 deleted the n_jobs_sorter branch May 21, 2024 13:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
concurrency Related to parallel processing sorters Related to sorters module
Projects
None yet
Development

Successfully merging this pull request may close these issues.

n_jobs not being respected between set_global_kwargs and running sorter
4 participants