Repository navigation
Memory Profiling #15866
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
Merged
Merged
Memory Profiling #15866
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
cf15a02
_cudf_nvtx_annotate: add RMM's memory profiler
madsbk f1ff8d4
Merge branch 'branch-24.08' of github.com:rapidsai/cudf into memory_s…
madsbk 32ca877
get_option("memory_profiling")
madsbk aeff5c7
Merge branch 'branch-24.08' of github.com:rapidsai/cudf into memory_s…
madsbk b323b28
_dask_cudf_performance_tracking
madsbk 386292b
doc
madsbk 3625db6
remove {py:mod}
madsbk 2a0c77a
Merge branch 'branch-24.08' of github.com:rapidsai/cudf into memory_s…
madsbk 7cae9d1
fix _external_only_api
madsbk a4e4fc6
use traceback.extract_stack(limit=2)[0]
madsbk 69a54f3
typo
madsbk 46d9471
don't call rmm.statistics.enable_statistics()
madsbk ffa6360
test
madsbk 6cf2ca3
Update docs/cudf/source/user_guide/memory-profiling.md
madsbk 339fa3e
doc
madsbk 124b179
Merge branch 'memory_statistics' of github.com:madsbk/cudf into memor…
madsbk 2fe290b
Update docs/cudf/source/user_guide/memory-profiling.md
madsbk 3f2ae60
Apply suggestions from code review
madsbk e3e3d73
Merge branch 'branch-24.08' of github.com:rapidsai/cudf into memory_s…
madsbk 944901a
remove nvtx_annotation.py
madsbk 3510e27
remove _cudf_nvtx_annotate
madsbk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
docs/cudf/source/user_guide/api_docs/performance_tracking.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| .. _api.performance_tracking: | ||
|
|
||
| ==================== | ||
| Performance Tracking | ||
| ==================== | ||
|
|
||
| .. currentmodule:: cudf.utils.performance_tracking | ||
| .. autosummary:: | ||
| :toctree: api/ | ||
|
|
||
| get_memory_records | ||
| print_memory_report |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| (memory-profiling-user-doc)= | ||
|
|
||
| # Memory Profiling | ||
|
|
||
| Peak memory usage is a common concern in GPU programming because GPU memory is typically smaller than available CPU memory. To easily identify memory hotspots, cuDF provides a memory profiler. It comes with an overhead so avoid using it in performance-sensitive code. | ||
|
|
||
| ## Enabling Memory Profiling | ||
|
|
||
| First, enable memory profiling in RMM by calling {py:func}`rmm.statistics.enable_statistics()`. This adds a statistics resource adaptor to the current RMM memory resource, which enables cudf to access memory profiling information. See the [RMM documentation](https://docs.rapids.ai/api/rmm/stable/guide/#memory-statistics-and-profiling) for more details. | ||
|
|
||
| Second, enable memory profiling in cudf by setting the `memory_profiling` option to `True`. Use {py:func}`cudf.set_option` or set the environment variable ``CUDF_MEMORY_PROFILING=1`` prior to the launch of the Python interpreter. | ||
|
madsbk marked this conversation as resolved.
Resolved
|
||
|
|
||
| To get the result of the profiling, use {py:func}`cudf.utils.performance_tracking.print_memory_report` or access the raw profiling data by using: {py:func}`cudf.utils.performance_tracking.get_memory_records`. | ||
|
|
||
| ### Example | ||
| In the following, we enable profiling, do some work, and then print the profiling results: | ||
|
|
||
| ```python | ||
| >>> import cudf | ||
| >>> from cudf.utils.performance_tracking import print_memory_report | ||
| >>> from rmm.statistics import enable_statistics | ||
| >>> enable_statistics() | ||
| >>> cudf.set_option("memory_profiling", True) | ||
| >>> cudf.DataFrame({"a": [1, 2, 3]}) # Some work | ||
| a | ||
| 0 1 | ||
| 1 2 | ||
| 2 3 | ||
| >>> print_memory_report() # Pretty print the result of the profiling | ||
| Memory Profiling | ||
| ================ | ||
|
|
||
| Legends: | ||
| ncalls - number of times the function or code block was called | ||
| memory_peak - peak memory allocated in function or code block (in bytes) | ||
| memory_total - total memory allocated in function or code block (in bytes) | ||
|
|
||
| Ordered by: memory_peak | ||
|
|
||
| ncalls memory_peak memory_total filename:lineno(function) | ||
| 1 32 32 cudf/core/dataframe.py:690(DataFrame.__init__) | ||
| 2 0 0 cudf/core/index.py:214(RangeIndex.__init__) | ||
| 6 0 0 cudf/core/index.py:424(RangeIndex.__len__) | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # Copyright (c) 2024, NVIDIA CORPORATION. | ||
|
|
||
| from io import StringIO | ||
|
|
||
| import pytest | ||
|
|
||
| import rmm.mr | ||
| import rmm.statistics | ||
|
|
||
| import cudf | ||
| from cudf.utils.performance_tracking import ( | ||
| get_memory_records, | ||
| print_memory_report, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def rmm_reset(): | ||
| """Fixture to reset the RMM resource before and after the test""" | ||
| mr = rmm.mr.get_current_device_resource() | ||
| try: | ||
| rmm.mr.set_current_device_resource(rmm.mr.CudaMemoryResource()) | ||
| yield | ||
| finally: | ||
| rmm.mr.set_current_device_resource(mr) | ||
|
|
||
|
|
||
| def test_memory_profiling(rmm_reset): | ||
| df1 = cudf.DataFrame({"a": [1, 2, 3]}) | ||
| assert len(get_memory_records()) == 0 | ||
|
|
||
| rmm.statistics.enable_statistics() | ||
| cudf.set_option("memory_profiling", True) | ||
|
|
||
| df1.merge(df1) | ||
|
|
||
| assert len(get_memory_records()) > 0 | ||
|
|
||
| out = StringIO() | ||
| print_memory_report(file=out) | ||
| assert "DataFrame.merge" in out.getvalue() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,30 +1,11 @@ | ||
| # Copyright (c) 2023, NVIDIA CORPORATION. | ||
| # Copyright (c) 2023-2024, NVIDIA CORPORATION. | ||
|
|
||
| import hashlib | ||
| from functools import partial | ||
|
|
||
| from nvtx import annotate | ||
|
|
||
| _NVTX_COLORS = ["green", "blue", "purple", "rapids"] | ||
|
|
||
|
|
||
| def _get_color_for_nvtx(name): | ||
| m = hashlib.sha256() | ||
| m.update(name.encode()) | ||
| hash_value = int(m.hexdigest(), 16) | ||
| idx = hash_value % len(_NVTX_COLORS) | ||
| return _NVTX_COLORS[idx] | ||
|
|
||
|
|
||
| def _cudf_nvtx_annotate(func, domain="cudf_python"): | ||
| """Decorator for applying nvtx annotations to methods in cudf.""" | ||
| return annotate( | ||
| message=func.__qualname__, | ||
| color=_get_color_for_nvtx(func.__qualname__), | ||
| domain=domain, | ||
| )(func) | ||
|
|
||
|
|
||
| _dask_cudf_nvtx_annotate = partial( | ||
| _cudf_nvtx_annotate, domain="dask_cudf_python" | ||
| from cudf.utils.performance_tracking import ( | ||
| _dask_cudf_performance_tracking, | ||
| _performance_tracking, | ||
| ) | ||
|
|
||
| # TODO: will remove this file and use _performance_tracking before merging | ||
|
vyasr marked this conversation as resolved.
Resolved
|
||
| _cudf_nvtx_annotate = _performance_tracking | ||
| _dask_cudf_nvtx_annotate = _dask_cudf_performance_tracking | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| # Copyright (c) 2023-2024, NVIDIA CORPORATION. | ||
|
|
||
| import contextlib | ||
| import functools | ||
| import hashlib | ||
| import sys | ||
| from typing import Dict | ||
|
|
||
| import nvtx | ||
|
|
||
| import rmm.statistics | ||
|
|
||
| from cudf.options import get_option | ||
|
|
||
| _NVTX_COLORS = ["green", "blue", "purple", "rapids"] | ||
|
|
||
|
|
||
| def _get_color_for_nvtx(name): | ||
| m = hashlib.sha256() | ||
| m.update(name.encode()) | ||
| hash_value = int(m.hexdigest(), 16) | ||
| idx = hash_value % len(_NVTX_COLORS) | ||
| return _NVTX_COLORS[idx] | ||
|
|
||
|
|
||
| def _performance_tracking(func, domain="cudf_python"): | ||
| """Decorator for applying performance tracking (if enabled).""" | ||
|
|
||
| @functools.wraps(func) | ||
| def wrapper(*args, **kwargs): | ||
| with contextlib.ExitStack() as stack: | ||
| if get_option("memory_profiling"): | ||
| # NB: the user still needs to call `rmm.statistics.enable_statistics()` | ||
| # to enable memory profiling. | ||
| stack.enter_context( | ||
| rmm.statistics.profiler( | ||
| name=rmm.statistics._get_descriptive_name_of_object( | ||
| func | ||
| ) | ||
| ) | ||
| ) | ||
| if nvtx.enabled(): | ||
| stack.enter_context( | ||
| nvtx.annotate( | ||
| message=func.__qualname__, | ||
| color=_get_color_for_nvtx(func.__qualname__), | ||
| domain=domain, | ||
| ) | ||
| ) | ||
| return func(*args, **kwargs) | ||
|
|
||
| return wrapper | ||
|
|
||
|
|
||
| _dask_cudf_performance_tracking = functools.partial( | ||
| _performance_tracking, domain="dask_cudf_python" | ||
| ) | ||
|
|
||
|
|
||
| def get_memory_records() -> ( | ||
| Dict[str, rmm.statistics.ProfilerRecords.MemoryRecord] | ||
| ): | ||
| """Get the memory records from the memory profiling | ||
|
|
||
| Returns | ||
| ------- | ||
| Dict that maps function names to memory records. Empty if | ||
| memory profiling is disabled | ||
| """ | ||
| return rmm.statistics.default_profiler_records.records | ||
|
|
||
|
|
||
| def print_memory_report(file=sys.stdout) -> None: | ||
| """Pretty print the result of the memory profiling | ||
|
|
||
| Parameters | ||
| ---------- | ||
| file | ||
| The output stream | ||
| """ | ||
| print(rmm.statistics.default_profiler_records.report(), file=file) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.