-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds Python bindings to Caliper with pybind11 (#573)
* Implements Python bindings for Caliper * Improves the exported type enums * Adds examples of the Python API * Fixes default parameter for cali_attr_properties * Removes default arugments for Attribute and Annotation in place of multiple constructors * Adds unit tests for Python bindings * Adds docstrings to Python bindings * Moves Python bindings to src/interface and adds logic to help with unit testing * Updates GH Actions runner to install Pybind11 correctly * Removes Variant from Python bindings * Removes redundant install of Pybind11 with apt * Enables bitwise arithmetic in bindings for cali_attr_properties
- Loading branch information
Showing
28 changed files
with
1,233 additions
and
5 deletions.
There are no files selected for viewing
This file contains 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 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 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,14 @@ | ||
import sys | ||
import sysconfig | ||
|
||
if len(sys.argv) != 3 or sys.argv[1] not in ("purelib", "platlib"): | ||
raise RuntimeError( | ||
"Usage: python get_python_install_paths.py <purelib | platlib> <sysconfig_scheme>" | ||
) | ||
|
||
install_dir = sysconfig.get_path(sys.argv[1], sys.argv[2], {"userbase": "", "base": ""}) | ||
|
||
if install_dir.startswith("/"): | ||
install_dir = install_dir[1:] | ||
|
||
print(install_dir, end="") |
This file contains 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 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,48 @@ | ||
# Copyright (c) 2024, Lawrence Livermore National Security, LLC. | ||
# See top-level LICENSE file for details. | ||
|
||
from pycaliper.high_level import annotate_function | ||
from pycaliper.annotation import Annotation | ||
|
||
import numpy as np | ||
|
||
@annotate_function() | ||
def init(arraySize: int, sort: bool) -> np.array: | ||
data = np.random.randint(256, size=arraySize) | ||
if sort: | ||
data = np.sort(data) | ||
return data | ||
|
||
|
||
@annotate_function() | ||
def work(data: np.array): | ||
data_sum = 0 | ||
for _ in range(100): | ||
for val in np.nditer(data): | ||
if val >= 128: | ||
data_sum += val | ||
print("sum =", data_sum) | ||
|
||
|
||
@annotate_function() | ||
def benchmark(arraySize: int, sort: bool): | ||
sorted_ann = Annotation("sorted") | ||
sorted_ann.set(sort) | ||
print("Intializing benchmark data with sort =", sort) | ||
data = init(arraySize, sort) | ||
print("Calculating sum of values >= 128") | ||
work(data) | ||
print("Done!") | ||
sorted_ann.end() | ||
|
||
|
||
@annotate_function() | ||
def main(): | ||
arraySize = 32768 | ||
benchmark(arraySize, True) | ||
benchmark(arraySize, False) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() | ||
|
This file contains 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,76 @@ | ||
# Copyright (c) 2024, Lawrence Livermore National Security, LLC. | ||
# See top-level LICENSE file for details. | ||
|
||
from pycaliper.high_level import annotate_function | ||
from pycaliper.config_manager import ConfigManager | ||
from pycaliper.instrumentation import ( | ||
set_global_byname, | ||
begin_region, | ||
end_region, | ||
) | ||
from pycaliper.loop import Loop | ||
|
||
import argparse | ||
import sys | ||
import time | ||
|
||
|
||
def get_available_specs_doc(mgr: ConfigManager): | ||
doc = "" | ||
for cfg in mgr.available_config_specs(): | ||
doc += mgr.get_documentation_for_spec(cfg) | ||
doc += "\n" | ||
return doc | ||
|
||
|
||
@annotate_function() | ||
def foo(i: int) -> float: | ||
nsecs = max(i * 500, 100000) | ||
secs = nsecs / 10**9 | ||
time.sleep(secs) | ||
return 0.5 * i | ||
|
||
|
||
def main(): | ||
mgr = ConfigManager() | ||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--caliper_config", "-P", type=str, default="", | ||
help="Configuration for Caliper\n{}".format(get_available_specs_doc(mgr))) | ||
parser.add_argument("iterations", type=int, nargs="?", default=4, | ||
help="Number of iterations") | ||
args = parser.parse_args() | ||
|
||
mgr.add(args.caliper_config) | ||
|
||
if mgr.error(): | ||
print("Caliper config error:", mgr, file=sys.stderr) | ||
|
||
mgr.start() | ||
|
||
set_global_byname("iterations", args.iterations) | ||
set_global_byname("caliper.config", args.caliper_config) | ||
|
||
begin_region("main") | ||
|
||
begin_region("init") | ||
t = 0 | ||
end_region("init") | ||
|
||
loop_ann = Loop("mainloop") | ||
|
||
for i in range(args.iterations): | ||
loop_ann.start_iteration(i) | ||
t *= foo(i) | ||
loop_ann.end_iteration() | ||
|
||
loop_ann.end() | ||
|
||
end_region("main") | ||
|
||
mgr.flush() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() | ||
|
This file contains 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 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,64 @@ | ||
set(PYCALIPER_BINDING_SOURCES | ||
annotation.cpp | ||
config_manager.cpp | ||
instrumentation.cpp | ||
loop.cpp | ||
mod.cpp | ||
) | ||
|
||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) | ||
|
||
find_package(pybind11 CONFIG REQUIRED) | ||
|
||
set(PYCALIPER_SYSCONFIG_SCHEME "posix_user" CACHE STRING "Scheme used for searching for pycaliper's install path. Valid options can be determined with 'sysconfig.get_scheme_names()'") | ||
|
||
execute_process(COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/cmake/get_python_install_paths.py purelib ${PYCALIPER_SYSCONFIG_SCHEME} OUTPUT_VARIABLE PYCALIPER_SITELIB) | ||
execute_process(COMMAND ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/cmake/get_python_install_paths.py platlib ${PYCALIPER_SYSCONFIG_SCHEME} OUTPUT_VARIABLE PYCALIPER_SITEARCH) | ||
|
||
message(STATUS "Pycaliper sitelib: ${PYCALIPER_SITELIB}") | ||
message(STATUS "Pycaliper sitearch: ${PYCALIPER_SITEARCH}") | ||
|
||
set(PYCALIPER_SITELIB "${PYCALIPER_SITELIB}/pycaliper") | ||
set(PYCALIPER_SITEARCH "${PYCALIPER_SITEARCH}/pycaliper") | ||
|
||
pybind11_add_module(__pycaliper_impl ${PYCALIPER_BINDING_SOURCES}) | ||
target_link_libraries(__pycaliper_impl PUBLIC caliper) | ||
target_compile_features(__pycaliper_impl PUBLIC cxx_std_11) | ||
target_include_directories(__pycaliper_impl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) | ||
|
||
add_custom_target( | ||
pycaliper_test ALL # Always build pycaliper_test | ||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/pycaliper | ||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/pycaliper ${CMAKE_CURRENT_BINARY_DIR}/pycaliper | ||
COMMENT "Copying pycaliper Python source to ${CMAKE_CURRENT_BINARY_DIR}/pycaliper" | ||
) | ||
add_dependencies(__pycaliper_impl pycaliper_test) | ||
|
||
if (BUILD_TESTING) | ||
set(PYPATH_TESTING ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "") | ||
add_custom_target( | ||
pycaliper_symlink_lib_in_build ALL | ||
COMMAND ${CMAKE_COMMAND} -E create_symlink | ||
$<TARGET_FILE:__pycaliper_impl> | ||
${CMAKE_CURRENT_BINARY_DIR}/pycaliper/$<TARGET_FILE_NAME:__pycaliper_impl> | ||
COMMENT "Creating symlink between Python C module and build directory for testing" | ||
DEPENDS __pycaliper_impl | ||
) | ||
message(STATUS "Will add ${PYPATH_TESTING} to PYTHONPATH during test") | ||
endif() | ||
|
||
install( | ||
DIRECTORY | ||
pycaliper/ | ||
DESTINATION | ||
${PYCALIPER_SITELIB} | ||
) | ||
|
||
install( | ||
TARGETS | ||
__pycaliper_impl | ||
ARCHIVE DESTINATION | ||
${PYCALIPER_SITEARCH} | ||
LIBRARY DESTINATION | ||
${PYCALIPER_SITEARCH} | ||
) |
Oops, something went wrong.