{"id":13533556,"url":"https://github.com/NAGAGroup/Scalix","last_synced_at":"2025-04-01T21:32:29.890Z","repository":{"id":153177326,"uuid":"599827150","full_name":"NAGAGroup/Scalix","owner":"NAGAGroup","description":"Scalix is a data parallel compute library that automatically scales to the available compute resources.","archived":false,"fork":false,"pushed_at":"2024-04-12T13:54:51.000Z","size":1115,"stargazers_count":7,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-13T23:10:34.697Z","etag":null,"topics":["cuda","hpc","scientific-computing"],"latest_commit_sha":null,"homepage":"","language":"Cuda","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NAGAGroup.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null}},"created_at":"2023-02-10T00:45:54.000Z","updated_at":"2024-04-15T02:32:32.877Z","dependencies_parsed_at":"2024-02-11T02:23:53.640Z","dependency_job_id":"11d265a9-e38c-4060-b987-610965fb0770","html_url":"https://github.com/NAGAGroup/Scalix","commit_stats":{"total_commits":141,"total_committers":1,"mean_commits":141.0,"dds":0.0,"last_synced_commit":"3d619da7d5775ccdaf6c1fefc044a14662419e05"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NAGAGroup%2FScalix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NAGAGroup%2FScalix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NAGAGroup%2FScalix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NAGAGroup%2FScalix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NAGAGroup","download_url":"https://codeload.github.com/NAGAGroup/Scalix/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246713458,"owners_count":20821893,"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","hpc","scientific-computing"],"created_at":"2024-08-01T07:01:20.959Z","updated_at":"2025-04-01T21:32:29.477Z","avatar_url":"https://github.com/NAGAGroup.png","language":"Cuda","funding_links":[],"categories":["Software"],"sub_categories":["Trends"],"readme":"# Welcome to SCALIX!\n---\n**Note**: This proof-of-concept version of Scalix has reached EOL and will no longer receive\nupdates/fixes, except potentially on an as-needed basis for current research that depends on this\nversion. The insights gained since starting this project, both through active development and\nreal-world use in a non-trivial codebase, have been incredibly invaluable. With those insights,\ndevelopment on a much improved version written in SYCL instead of CUDA has begun on the sycl\nbranch. This version will be better in nearly every way, some of the improvements include:\n\n- SYCL is a more modern API, using ISO compliant C++ instead of an extended C-like language with\n  some C++ support like CUDA. Not only does this enable the nice-to-haves of modern C++, but the\n  new implementation will be platform agnostic, enabling a wider range of hardware, and likely\n  even more added hardware support in the future with no added development of Scalix\n- Using the SYCL specification as a guide, Scalix will use the buffer/accessor model, rather\n  than relying on CUDA's Unified Memory(UM)/On-demand page migration. This will allow the Scalix\n  runtime to maintain a dependency graph and explicitly manage data in an optimal way defined by\n  that dependency graph. While Scalix currently scales well with relatively simple access patterns,\n  the UM driver heuristics really break down for more complex problems leading to inefficient data\n  transfers that tank performance\n- Way simpler API that is both easier to use and to maintain/test. If you know SYCL, you'll feel right\n  at home with Scalix\n- Much better thread management. Currently, Scalix spawns three or four nested threads for a single kernel, which\n  is already pretty bad, but the threads are spawned via `std::async` instead of a dedicated threadpool. While the\n  true cost of this is unclear, it is a pretty bad implementation that has a lot of room for improvement and makes\n  profiling incredibly difficult in NSight as the number of CUDA streams balloons very quickly. Going forward,\n  Scalix will internally use a multi-threading approach that is akin to an actor model and we have been able to\n  avoid spawning any threads until absolutely necessary, completely avoiding any need for async tasks to spin in\n  some thread waiting on a set of dependent futures before proceeeding. It also enables a stateless implementation\n  where the dependency graph is implicitly defined via shared/unique locks around protected data.\n  Additionally, we will likely be incorporating a threadpool for even better thread management.\n- Well-thought out API/system design and proper unit-testing, static analysis, etc from the start. With the first\n  attempt at Scalix, it was being developed in parallel with a research project, and often times getting something\n  that worked, even if not ideal, was priortized over well-thought out solutions. Well, that version of Scalix works\n  well enough for the research project, at least until the first working release of SYCL-Scalix, so we can take\n  our time. This should give more confidence to future users that the project is properly handled, and will make\n  getting started with the software much easier.\n- More opportunities for optimization, some implicit, some explicit:\n    - We will enforce that only the minimally-required memory to keep data valid will be used unless explicitly declared by the user.\n    - The way memory is handled will have very clear, documented behavior, allowing users to fine-tune their code for these rules.\n    - Only the memory locations being accessed on each device will be allocated, where these accesses are specified by either a few\n      simple but commonly used strategies or a more flexible strategy where the user provides a custom command that defines accessed indices.\n    - In terms of optimization of compute resources, Scalix development will be focused on providing default and user-defined \"knobs\"\n      that can be tweaked to find the optimal balance of resources\n---\n\nScalix is a data parallel computing framework designed to provide an easy-to-use\ninterface for designing data parallel applications that automatically scale to\nthe available compute resources. Data distribution and load balancing are\nabstracted away from the user, allowing them to focus on the application logic.\nScalix is designed to be written using \"kernel style\" programming, where the\nuser writes a single function that is executed on a grid of threads.\n\nCurrently, Scalix is only supported on a single machine with multiple CUDA\ndevices that must support Unified Memory. Furthermore, while not strictly\nrequired, it is recommended to use Linux and a CUDA device with compute\ncapability 6.0 or higher so that we can take advantage of on-demand page\nmigration. Performance without on-demand page migration is not tested, but will\nassuredly be slower.\n\nIn the future, we plan to migrate to a SYCL-based implementation + MPI. This\nfuture specification will look drastically different as we won't be able to rely\non Unified Memory, but many of the concepts will remain the same. Specifically,\nthe user should be able to write for an abstract \"device\" that may be one or\nmany actual compute devices.\n\nEven further in the future we plan to define a generic specification for which\nanyone can write their own implementation.\n\nThink of this current version of Scalix as a proof of concept.\n\nFor a more detailed outline of our roadmap, see [ROADMAP.md](ROADMAP.md).\n\n## Getting Started\n\nFor the moment, this is a header-only library, but that may change in the\nfuture. Any program that uses Scalix must be compiled with the nvcc flag\n`--extended-lambda`. The library is written in C++17. We have provided a\n`CMakeLists.txt` file that provides the `scalix` interface library that, when\nadded as a link to a target, will automatically add the `--extended-lambda` flag\nto the nvcc compiler. There are also a number of options that can be used to do\nthings like emulate multiple devices to check for correctness. These options are\ndescribed in the `CMakeLists.txt` file.\n\n### Writing a Scalix Program\n\nIn the following program we add one to all elements of a Scalix array.\n\n```c++\n#include \u003cscalix/scalix.cuh\u003e\n\nint main() {\n    // create a 2D array of 1000x1000 elements\n    //\n    // the unified memory hints for the array are set such as to maximize\n    // performance for equally distributed access across all devices\n    sclx::array\u003cint, 2\u003e arr({1000, 1000});\n\n    // initialize the array to 0\n    //\n    // kernels automatically distribute the work across all devices\n    // for the result array\n    sclx::execute_kernel([\u0026](sclx::kernel_handler\u0026 handler) {\n        handler.launch(\n            sclx::md_range_t\u003c2\u003e(arr.shape()),\n            arr,  // need to provide a result array to guide the distribution of\n                  // work\n            [=] __device__(const sclx::md_index_t\u003c2\u003e\u0026 idx,\n                           /*see below for what this is*/ const auto\u0026) {\n                arr[idx] = 0;\n            }\n        );\n    });\n\n    // we also provide a kernel info object (sclx::kernel_info\u003cRangeRank,\n    // ThreadBlockRank\u003e) add one to all elements and print out the block thread\n    // id\n    sclx::execute_kernel([\u0026](sclx::kernel_handler\u0026 handler) {\n        handler.launch(\n            sclx::md_range_t\u003c2\u003e(arr.shape()),\n            arr,\n            [=] __device__(\n                const sclx::md_index_t\u003c2\u003e\u0026 idx,\n                const sclx::kernel_info\u003c\n                    2,\n                    sclx::cuda::traits::kernel::default_block_shape.rank()\u003e\u0026\n                    info\n            ) {\n                printf(\n                    \"block thread id: %u\\n\",\n                    static_cast\u003cuint\u003e(info.local_thread_id()[0])\n                );\n                arr[idx] += 1;\n            }\n        );\n    });\n}\n```\n\nA special type of problem one may face is one where the write pattern cannot fit\ninto a neat range. For example, suppose we want to generate a histogram for some\nvalues in an array. Sequentially, we would do something like iterate over each\nvalue, determine the bin it belongs to, and increment the value of that bin.\n\nTo do this in Scalix, we instead would use the \"index generator\"-based kernel\nAPI. The index generator in this case would take an index from the range\nassociated with the values and return the bin index. Our kernel implementation\nreplicates the entire range over each device, but only calls the provided\nfunctor if the write index is local to the device. So, unfortunately, this means\nscaling will not be ideal, and in many cases worse than a single device\nimplementation. However, what this does allow is the prevention of expensive\nmemory transfers for a problem that mostly scales well using the range-based\nAPI.\n\n```c++\n#include \u003cscalix/scalix.cuh\u003e\n\n// required interface for an index generator\nclass histogram_index_generator {\n  public:\n    static constexpr uint range_rank = 1;\n    static constexpr uint index_rank = 1;\n\n    __host__ __device__ const sclx::md_range_t\u003crange_rank\u003e\u0026 range() const;\n\n    __host__ __device__ const sclx::md_range_t\u003cindex_rank\u003e\u0026 index_range() const;\n\n    __host__ __device__ sclx::md_index_t\u003cindex_rank\u003e\n    operator()(const sclx::md_index_t\u003crange_rank\u003e\u0026) const;\n\n    ...\n};\n\nint main() {\n\n    sclx::array\u003cint, 1\u003e values({1000});\n    sclx::array\u003cint, 1\u003e histogram({100});\n\n    histogram_index_generator index_generator(...);\n\n    sclx::execute_kernel([\u0026](sclx::kernel_handler\u0026 handler) {\n        handler.launch(\n            index_generator,\n            histogram,\n            [=] __device__(const sclx::md_index_t\u003c1\u003e\u0026 histogram_idx, const auto\u0026) {\n                atomicAdd(\u0026histogram[histogram_idx], 1);\n            }\n        );\n    });\n}\n\n```\n\nThe [examples](examples) directory contains a number of examples that\ndemonstrate how to use the library. For every new feature we add, we will do our\nbest to add an example that demonstrates its use. As we approach the first\nstable release, we will start to focus on adding proper documentation.\n\n## Performance\n\nWe have shown almost perfect strong scaling for the `distributed_access` example\nin the repo, distributed across 2 GPUs. The example has two arrays of interest.\nEach kernel reads 64 elements from one array and atomically adds it to an\nelement of the other array for each element in the result array. A portion of\nthese reads happen across device boundaries. Also note that the source and\nresult arrays exchange roles for each kernel, showing we can still get good\nperformance, even for arrays that read/write equally. Finally, a distributed\nreduction algorithm is used to sum the values of one of the arrays together just\nfor fun (and to check that the solution is correct). The following times were\nrecorded on 1 vs 2 RTX 3090s, respectively.\n\n```\n1 GPU time: 163.1ms\n2 GPU time: 87.5ms\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNAGAGroup%2FScalix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNAGAGroup%2FScalix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNAGAGroup%2FScalix/lists"}