{"id":19262411,"url":"https://github.com/alpaka-group/bactria","last_synced_at":"2025-04-21T18:31:07.269Z","repository":{"id":42325173,"uuid":"296391156","full_name":"alpaka-group/bactria","owner":"alpaka-group","description":"Broadly Applicable C++ Tracing and Instrumentation API :camel:","archived":false,"fork":false,"pushed_at":"2023-12-10T09:48:48.000Z","size":2583,"stargazers_count":8,"open_issues_count":6,"forks_count":2,"subscribers_count":5,"default_branch":"develop","last_synced_at":"2025-04-01T14:47:25.064Z","etag":null,"topics":["cuda","hardware-counters","instrumentation-api","metrics","rocm","tracing-events"],"latest_commit_sha":null,"homepage":"https://alpaka-group.github.io/bactria/","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"eupl-1.2","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alpaka-group.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-17T17:05:01.000Z","updated_at":"2025-03-12T19:59:35.000Z","dependencies_parsed_at":"2022-09-17T17:12:38.599Z","dependency_job_id":null,"html_url":"https://github.com/alpaka-group/bactria","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpaka-group%2Fbactria","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpaka-group%2Fbactria/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpaka-group%2Fbactria/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpaka-group%2Fbactria/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alpaka-group","download_url":"https://codeload.github.com/alpaka-group/bactria/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250110870,"owners_count":21376547,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cuda","hardware-counters","instrumentation-api","metrics","rocm","tracing-events"],"created_at":"2024-11-09T19:31:28.315Z","updated_at":"2025-04-21T18:31:06.982Z","avatar_url":"https://github.com/alpaka-group.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bactria - Broadly Applicable C++ Tracing and Instrumentation API\n\n[![Language](https://img.shields.io/badge/-C++14-5E97D0?logo=C%2B%2B\u0026logoColor=white\u0026style=for-the-badge)](https://isocpp.org/)\n[![Platforms](https://img.shields.io/badge/platform-linux%20|%20windows%20|%20mac-lightgrey?style=for-the-badge)](https://github.com/alpaka-group/bactria)\n[![License](https://img.shields.io/github/license/alpaka-group/bactria?color=003399\u0026style=for-the-badge)](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12)\n\n## About this project\n\n### Introduction\n\nThe **bactria** library is a header-only C++14 library for profiling and tracing. By annotating segments of your code\nwith bactria's classes you can gather fine-grained information about your application's performance without\nintroducing runtime overhead in other program parts.\n\nbactria itself is platform-independent and provides a unified modern C++ API to the user. The profiling and/or tracing\ninformation are collected by its various plugins:\n\n  * JSON: Supported on all platforms. Used for saving user-defined metrics to disk.\n  * stdout: Supported on all platforms. Used for tracing events and time spans and printing them to stdout.\n  * Score-P: Supported on Linux. Used for collecting various metrics (such as hardware counters) and saving them to\n    disk for later analysis.\n  * NVTX: Supported on all platforms. Used for tracing events and time spans and visualizing them on NVIDIA's visual\n    profilers.\n  * rocTX: Supported on Linux. Used for tracing events and time spans and visualizing them on Chrome's `about:tracing`\n    tool (used by AMD's ROCm).\n\n### Differences to similar projects\n\nTODO: Fill this out!\n\n## Getting started\n\n### Prerequisites\n\nThe user-facing API has no dependencies. However, most plugins require [`toml11`](https://github.com/ToruNiina/toml11)\nto be present and additionally introduce their platform-specific dependencies (such as Score-P, CUDA or ROCm).\n\nbactria assumes that all builds happen out-of-source. The easiest way to achieve this is to create a `build` directory\nin bactria's top-level directory:\n\n```zsh\ngit clone https://github.com/alpaka-group/bactria.git\ncd bactria\nmkdir build \u0026\u0026 cd build\n```\n\n### Configuration\n\nbactria uses CMake (\u003e=3.18) as a build system. On top of the common CMake build options (such as the build type) it\nsupports the following configuration switches:\n\n* `bactria_BUILD_DOCUMENTATION` -- Build the Doxygen documentation. Default: `ON`.\n* `bactria_BUILD_EXAMPLES` -- Build the examples (see the `examples` folder). Default: `ON`.\n* `bactria_CUDA_PLUGINS` -- Build the CUDA ecosystem plugins. Default: `OFF`\n* `bactria_JSON_PLUGINS` -- Build the JSON-based plugins. Default: `ON`\n  * `bactria_SYSTEM_JSON` -- Use your local installation of the nlohnmann-json library. If set to `OFF`, bactria will\n    attempt to download the library to its build directory. Default: `ON`.\n* `bactria_ROCM_PLUGINS` -- Build the ROCm ecosystem plugins. Default: `OFF`.\n* `bactria_SCOREP_PLUGINS` -- Build the Score-P plugins. Default: `OFF`.\n* `bactria_STDOUT_PLUGINS` -- Build the `stdout` plugins. Default: `ON`.\n  * `bactria_SYSTEM_FMT` -- Use your local installation of `{fmt}` if the `stdout` plugins are being built. If set to\n    `OFF`, bactria will attempt to download the library to its build directory. Default: `ON`.\n* `bactria_SYSTEM_TOML11` -- Use your local installation of toml11. If set to `OFF`, bactria will attempt to download\n  the library to its build directory. Default: `ON`.\n\nThe following example configures the build system for building the Doxygen documentation, the examples and the plugins\nfor CUDA, JSON, Score-P and `stdout` in `Release` mode:\n\n```zsh\ncmake -DCMAKE_BUILD_TYPE=Release -Dbactria_CUDA_PLUGINS=ON -Dbactria_SCOREP_PLUGINS=ON ..\n```\n\n### Building\n\nIf the previous step was successful all that is left to do is invoke the actual build command:\n\n```zsh\ncmake --build . --config Release -j[number of parallel jobs]\n```\n\n## Usage\n\nAfter a successful bactria build the contents of your `build` directory will look similar to this structure (Visual\nStudio and XCode builds may have another intermediate directory between `build` and the subdirectories here):\n\n```\nbuild/\n|\n----.cmake/\n----CMakeFiles/\n----examples/\n----src/\n----[some files]/\n```\n\nYou should be interested in the contents of `examples` and `src`. In the subdirectories of the `examples` folder you\nwill find executables which already have built-in bactria support. In the subdirectories of the `src` folder you will\nfind the plugins that were built according to your configuration:\n\n```\nbuild/\n|\n----examples/\n|   |\n|   ----simpleLoop/\n|       |\n|       ----simpleLoop\n----src/\n    |\n    ----metrics/\n    |   |\n    |   ----scorep/\n    |       |\n    |       ----libbactria_metrics_scorep.so\n    ----ranges/\n    |   |\n    |   ----nvtx/\n    |   |   |\n    |   |   ----libbactria_ranges_nvtx.so\n    |   ----roctx/\n    |   ----stdout/\n    |       |\n    |       ----libbactria_ranges_stdout.so\n    ----reports/\n        |\n        ----json/\n            |\n            ----libbactria_reports_json.so\n```\n\n### Activating bactria plugins\n\nSwitch to the directory with the built `simpleLoop` example:\n\n```\ncd examples/simpleLoop\n```\n\nIf you just execute the program without any further configuration you will notice that there are no additional output\nfiles produced. This is a design principle: If you do not want to use a certain aspect of bactria you do not have to!\nInternally, bactria will disable this functionality if no plugin was selected at runtime.\n\nTo enable bactria's plugins you have to set one (or more) of the following environment variables to the path of your\ndesired plugin:\n\n```\nexport BACTRIA_METRICS_PLUGIN=/path/to/bactria/build/src/metrics/scorep/libbactria_metrics_scorep.so\nexport BACTRIA_RANGES_PLUGIN=/path/to/bactria/build/src/ranges/nvtx/libbactria_ranges_nvtx.so\nexport BACTRIA_REPORTS_PLUGIN=/path/to/bactria/build/src/reports/json/libbactria_reports_json.so\n./simpleLoop\n```\n\nAfter the program execution you should see some additional files in the directory that have not been present before.\nThese are the files you can now load into your favourite analysis / profiling tools for further examination.\n\nIn the next sections we will explain the concepts behind `metrics`, `ranges` and `reports`.\n\n### Initialization\n\nBefore you can use bactria you have to initialize the library. This is done by creating a `Context` (once per process)\nand keeping it alive until you no longer require any functionality from bactria. The `Context` takes care of loading\nyour selected plugin(s) into memory so you can make use of bactria's user API. The easiest way for managing a bactria\n`Context` is to create it at the beginning of `main` and keep it alive until the program stops:\n\n```c++\n#include \u003cbactria/bactria.hpp\u003e\n\nauto main() -\u003e int\n{\n    try\n    {\n        auto ctx = bactria::Context{};\n        auto ctx2 = ctx; // This is okay; Context's internals are reference-counted\n\n        foo();\n\n        // End of scope: ctx is destroyed and automatically shuts down bactria.\n    }\n    catch(std::runtime_error const\u0026 err)\n    {\n        std::cerr \u003c\u003c err.what() \u003c\u003c std::endl;\n        return EXIT_FAILURE;\n    }\n    \n    return EXIT_SUCCESS;\n}\n```\n\nNote that the context is wrapped into a `try`/`catch` block. Should any internal errors occur in bactria's user-facing\nparts a `std::runtime_error` will be thrown.\n\n### Ranges\n\nbactria's ranges are a useful tool if you want to highlight / visualize certain events and time spans (= ranges) in\nyour application code. This gives you a high-level view onto your program's behaviour and can help you with choosing\nthe correct code segments to analyse in more detail.\n\n* `Event`s are single points in time and are simply triggered / `fire`d in the application code.\n* `Range`s are time spans and are `start`ed and `stop`ped.\n* Both `Event`s and `Range`s can be assigned to a `Category`. Through the configuration file you can filter out all\n  `Event`s and `Range`s part of a specific `Category`.\n\n`Event`s and `Range`s can freely overlap / be nested in any way you feel necessary. This is how it looks like in code:\n\n```c++\nauto foo()\n{\n    using namespace bactria::ranges;\n\n    // After construction func_range is immediately started.\n    auto func_range = Range{\"Function foo()\", color::orange};\n\n    // Construct an event belonging to a category.\n    auto cat_func_call = Category{/* id = */ 42, /* name = */ \"function call\"};\n    auto call_event = Event{\"Called bar()\", color::green, cat_func_call};\n    \n    // Call bar once\n    bar();\n    call_event.fire(__FILE__, __LINE__, __func__);\n\n    // Call bar again -- will show up as separate event on profiler\n    bar();\n    call_event.fire(__FILE__, __LINE__, __func__);\n\n    // For one-time events there is a convenience macro that removes the __FILE__ __LINE__ __func__ boilerplate\n    baz();    \n    bactria_Event(\"Called baz()\", color::blue, cat_func_call);\n\n    // Ranges can overlap\n    auto r1 = Range{\"Some range\", color::red};\n    auto r2 = Range{\"Another range\", color::cyan};\n\n    // Depending on condition one range is stopped now, the other when it leaves the scope.\n    if(condition)\n        r1.stop();\n    else\n        r2.stop();\n\n    // End of scope: func_range and r1 or r2 are automatically stopped.\n}\n```\n\nAs you may have noticed we have supplied a color to the range / event constructor. Some plugins support custom colors\nto enhance the visualizer output (this depends on vendor APIs and is therefore not supported by all available\nplugins). You can either use one of bactria's numerous pre-defined colors (see `include/bactria/ranges/Colors.hpp`) or\nsupply your own color in ARGB format:\n\n```c++\nconstexpr auto my_orange = 0xFFFFA500u;\n                         //  ^^^^^^^^\n                         //  AARRGGBB\n```\n\n### Metrics\n\nOnce you have an idea of where your program spends most of its time you might want to optimize these portions. In\norder to find the major bottlenecks it is useful to look at certain metrics like hardware counters, a more detailed\nprofiling, call stacks, and so on. Plugins implementing bactria's `metrics` functionality are built on top of various\nvendors' APIs dedicated to this purpose. By using bactria's `metrics` API you can make use of these APIs in a portable\nway.\n\nIn the `metrics` API the following classes are available:\n\n* `Sector`s are used as annotations in your code and enable the detailed collection of metrics by your ecosystem's\n  performance tools.\n* `Tag`s are a special kind of metadata that some plugins can make use of. By default, all `Sector`s are assigned the\n  `Generic` tag. Some plugins understand additional information supplied by other `Tag`s, such as `Function`,  `Loop`,\n  `Body`, and so on, to provide you with more detailed results.\n* `Phase`s are used to group `Sector`s (possibly in different scopes) into logical program phases. Some performance\n  tools can make use of this information to provide you with an analysis of these logical segments.\n\nBoth `Sector`s and `Phase`s follow a stack-based / LIFO-based programming approach. This means that they have to be\ncorrectly nested and cannot overlap freely (in contrast to the `ranges` API).\n\nExample:\n\n```c++\nauto bar()\n{\n    using namespace bactria::metrics;\n\n    // Define logical phase and enter it immediately.\n    auto p1 = Phase{\"first_bar_half\", __FILE__, __LINE__, __func__};\n\n    // Define logical phase, but do not enter it.\n    auto p2 = Phase{\"second_bar_half\"};\n\n    // Once successfully constructed this sector will start collecting metrics right away.\n    auto s1 = Sector\u003cFunction\u003e{\"bar\", __FILE__, __LINE__, __func__};\n\n    // Non-entering constructor: This sector needs to be entered manually at a later point. It will not collect any\n    // metrics right away.\n    auto s2 = Sector\u003cLoop\u003e{\"some_sector\"};\n\n    /*\n     * Do some work\n     */\n    // s2.enter(__FILE__, __LINE__, __func__); // \u003c-- This is very verbose. Fortunately there is a convenience macro:\n    bactria_Enter(s2);\n    for(auto i = 0; i \u003c 20; ++i)\n    {\n        /* ... */\n    }\n    // s2.leave(__FILE__, __LINE__, __func__); // \u003c-- Same as above\n    bactria_Leave(s2);\n\n    /* Wrong order! Wrongly nested.\n    bactria_Enter(p2);\n    bactria_Leave(p1);\n    */\n\n    // Right order: Leave first phase and enter second phase\n    bactria_Leave(p1);\n    bactria_Enter(p2);\n\n    // Collect metrics for every iteration of a loop body\n    auto s3 = Sector\u003cBody\u003e{\"loop_body\"};\n    for(auto i = 0; i \u003c 20; ++i)\n    {\n        bactria_Enter(s3);\n        /* Do work */\n        bactria_Leave(s3);\n    }\n\n    // End of scope: p2 is left automatically\n}\n```\n\n### Reports\n\nSometimes the metrics collected by the various vendor-specific plugins are not enough. For this case bactria provides\nthe `reports` API which enables you to save key-value pairs (where `key` is a `std::string` and `value` an arithmetic\ntype or a `std::string`). To do this, you first create a `IncidentRecorder` and use it to create `Incident`s (the key-value pairs).\nOnce your recording is complete you can submit a `Report` (which matches the output file generated by the plugin):\n\n```c++\nauto baz()\n{\n    using namespace bactria::reports;\n\n    using clock = std::high_resolution_clock;\n\n    // Define all types which are stored between recording steps. The last type must be an Incident\n    using Recorder = bactria::reports::IncidentRecorder\u003c\n        typename clock::time_point,\n        typename std::chrono::nanoseconds::rep,\n        bactria::reports::Incident\u003cdouble\u003e,\n        bactria::reports::Incident\u003cint\u003e,\n        bactria::reports::Incident\u003cint\u003e\u003e;\n    auto ir = Recorder{};\n\n    // Extract record type from recorder. Our functors can use this to access the recorded values.\n    using Record = typename Recorder::record_t;\n\n    for(auto i = 0; i \u003c 20; ++i)\n    {\n        // Start timer\n        ir.record_step([](Record\u0026 r) {\n            // Store the clock::time_point in the recorder. The index corresponds to the element order defined\n            // in the using Recorder = ... directive above.\n            r.store\u003c0\u003e(clock::now());\n        });\n\n        // Stop timer\n        ir.record_step([](Record\u0026 r) {\n            // Load the clock::time_point from the recorder.\n            auto const start = r.load\u003c0\u003e();\n            auto const end = clock::now();\n            auto const dur = std::chrono::duration_cast\u003cstd::chrono::nanoseconds\u003e(end - start);\n\n            // Store the nanoseconds\n            r.store\u003c1\u003e(dur.count());\n        });\n\n        // Do something else with no storage requirements\n        ir.record_step([]() { std::cout \u003c\u003c \"Something else...\" \u003c\u003c std::endl; });\n\n        // Calculate average\n        ir.record_step([\u0026](Record\u0026 r) {\n            // Load the nanoseconds\n            auto const dur = r.load\u003c1\u003e();\n            avgLoopTime += dur;\n\n            std::cout \u003c\u003c \"Hello, Incident!\" \u003c\u003c std::endl;\n\n            if(i \u003e 2 \u0026\u0026 (i + 1) % 5 == 0)\n            {\n                auto const avg = avgLoopTime / 5.0;\n                avgLoopTime = 0.0;\n\n                // Save three different incidents we are interested in\n                r.store\u003c2\u003e(bactria::reports::make_incident(\"Average\", avg));\n                r.store\u003c3\u003e(bactria::reports::make_incident(\"Step begin\", i - 5 + 1));\n                r.store\u003c4\u003e(bactria::reports::make_incident(\"Step end\", i + 1));\n\n                // Generate a report. The string (without any extensions) may be used to generate a filename\n                // Make sure you include all incident indices you are interested in.\n                // Repeated calls to this function with the same name string will append to the already\n                // existing file (if any).\n                r.submit_report\u003c2, 3, 4\u003e(\"loop_average\");\n            }\n        });\n    }\n}\n```\n\n## Contributors\n\n### Maintainers and Core Developers\n\n* Jan Stephan (original author)\n\n### Former Members, Contributions and Thanks\n\n* Dr. Michael Bussmann\n* René Widera\n\n## Acknowledgements\n\nThis work was partially funded by the [Center of Advanced Systems Understanding (CASUS)](https://www.casus.science)\nwhich is financed by [Germany's Federal Ministry of Education and Research (BMBF)](https://www.bmbf.de/en/index.html)\nand by the [Saxon Ministry for Science, Culture and Tourism (SMWK)](https://www.smwk.sachsen.de) with tax funds on the\nbasis of the budget approved by the [Saxon State Parliament](https://www.landtag.sachsen.de/en/).\n\n## Licence\n\nThis free software is licensed unter the EUPL v1.2. Please refer to the `LICENSE` file in this directory for the\nconcrete details of this licence.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpaka-group%2Fbactria","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falpaka-group%2Fbactria","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpaka-group%2Fbactria/lists"}