{"id":21915302,"url":"https://github.com/tim-harding/soa-rs","last_synced_at":"2025-05-16T09:04:21.662Z","repository":{"id":214679039,"uuid":"729272522","full_name":"tim-harding/soa-rs","owner":"tim-harding","description":"An SoA library for Rust","archived":false,"fork":false,"pushed_at":"2025-03-01T18:02:46.000Z","size":424,"stargazers_count":140,"open_issues_count":2,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-08T19:06:40.732Z","etag":null,"topics":["data-oriented-design","soa","structure-of-arrays"],"latest_commit_sha":null,"homepage":"https://docs.rs/soa-rs/latest/soa_rs/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tim-harding.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-08T19:22:18.000Z","updated_at":"2025-04-25T16:34:10.000Z","dependencies_parsed_at":"2024-05-09T23:23:01.009Z","dependency_job_id":"eacf8e52-5a23-4ecd-8cba-f55c41ffcd0d","html_url":"https://github.com/tim-harding/soa-rs","commit_stats":null,"previous_names":["tim-harding/soapy","tim-harding/soa-rs"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tim-harding%2Fsoa-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tim-harding%2Fsoa-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tim-harding%2Fsoa-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tim-harding%2Fsoa-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tim-harding","download_url":"https://codeload.github.com/tim-harding/soa-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254501557,"owners_count":22081528,"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":["data-oriented-design","soa","structure-of-arrays"],"created_at":"2024-11-28T19:10:50.627Z","updated_at":"2025-05-16T09:04:21.642Z","avatar_url":"https://github.com/tim-harding.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![docs.rs](https://img.shields.io/docsrs/soa-rs)](https://docs.rs/soa-rs/latest/)\n[![Crates.io Version](https://img.shields.io/crates/v/soa-rs)](https://crates.io/crates/soa-rs/)\n[![GitHub License](https://img.shields.io/github/license/tim-harding/soa-rs)](https://choosealicense.com/licenses/mit/)\n\n# soa-rs\n\nsoa-rs makes it simple to work with the structure-of-arrays memory layout. What\n`Vec\u003cT\u003e` is to array-of-structures (AoS), `Soa\u003cT\u003e` is to structure-of-arrays\n(SoA).\n\n## Example\n\n```rust\nuse soa_rs::{Soars, soa, AsSlice};\n\n// Derive soa-rs for your type\n#[derive(Soars, PartialEq, Debug)]\n#[soa_derive(Debug, PartialEq)]\nstruct Baz {\n    foo: u16,\n    bar: u8,\n}\n\n// Create the SoA\nlet mut soa = soa![\n    Baz { foo: 1, bar: 2 },\n    Baz { foo: 3, bar: 4 },\n];\n\n// Each field has a slice\nassert_eq!(soa.foo(), [1, 3]);\nassert_eq!(soa.bar(), [2, 4]);\n\n// Tuple structs work too\n#[derive(Soars, PartialEq, Debug)]\n#[soa_derive(Debug, PartialEq)]\nstruct Tuple(u16, u8);\nlet tuple = soa![Tuple(1, 2), Tuple(3, 4), Tuple(5, 6), Tuple(7, 8)];\n\n// SoA can be sliced and indexed like normal slices\nassert_eq!(tuple.idx(1..3), soa![Tuple(3, 4), Tuple(5, 6)]);\nassert_eq!(tuple.idx(3), TupleRef(\u00267, \u00268));\n\n// Drop-in for Vec in many cases\nsoa.insert(0, Baz { foo: 5, bar: 6 });\nassert_eq!(soa.pop(), Some(Baz { foo: 3, bar: 4 }));\nassert_eq!(soa, soa![Baz { foo: 5, bar: 6 }, Baz { foo: 1, bar: 2 }]);\nfor mut el in \u0026mut soa {\n    *el.foo += 10;\n}\nassert_eq!(soa, soa![Baz { foo: 15, bar: 6 }, Baz { foo: 11, bar: 2}]);\n```\n\n## What is SoA?\n\nWhereas AoS stores all the fields of a type in each element of the array,\nSoA splits each field into its own array. For example, consider\n\n```rust\nstruct Example {\n    foo: u8,\n    bar: u64,\n}\n```\n\nIn order to have proper memory alignment, this struct will have the following\nlayout. In this extreme example, almost half of the memory is wasted to padding.\n\n```text\n╭───┬───────────────────────────┬───────────────────────────────╮\n│foo│         padding           │              bar              │\n╰───┴───────────────────────────┴───────────────────────────────╯\n```\n\nBy using SoA, the fields will be stored separately, removing the need for\npadding:\n\n```text\n╭───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬┄\n│foo│foo│foo│foo│foo│foo│foo│foo│foo│foo│foo│foo│foo│foo│foo│foo│\n╰───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴┄\n╭───────────────────────────────┬───────────────────────────────┬┄\n│             bar               │              bar              │\n╰───────────────────────────────┴───────────────────────────────┴┄\n```\n\n## Performance\n\nIn addition to lowering memory usage, there are several reasons why SoA can\noffer better performance:\n\n- By removing padding, each cacheline is typically more information-dense.\n- When accessing only a subset of the available fields, only data for those\n  fields will be fetched.\n\nSoA does not offer performance wins in all cases. In particular, operations such\nas `push` and `pop` are usually slower than for `Vec` since the memory for each\nfield is far apart. SoA is most appropriate when either\n\n- Sequential access is the common access pattern\n- You are frequently accessing or modifying only a subset of the fields\n\n### SIMD vectorization\n\nSoA makes getting data into and out of SIMD registers trivial. Since values are\nstored sequentially, loading data is as simple as reading a range of memory into\nthe register. This bulk data transfer is very amenable to auto-vectorization. In\ncontrast, AoS stores fields at disjoint locations in memory. Therefore,\nindividual fields must be individually copied to different positions within the\nregisters and, later, shuffled back out in the same way. This can prevent the\ncompiler from applying vectorization. For this reason, SoA is much more likely\nto benefit from SIMD optimizations.\n\n### Examples\n\n#### Zig\n\nSoA is a popular technique in data-oriented design. Andrew Kelley gives a\nwonderful [talk](https://vimeo.com/649009599) describing how SoA and other\ndata-oriented design patterns earned him a 39% reduction in wall clock time\nin the Zig compiler.\n\n#### Benchmark\n\n`soa-rs-testing` contains a\n[benchmark](https://github.com/tim-harding/soa-rs/blob/92c12415d1fb8b9f2a015b35ff02a23b0e3aaa96/soa-rs-testing/benches/benchmark.rs#L82-L88)\ncomparison that sums the dot products of 2¹⁶ 4D vectors. The `Vec` version runs\nin 132µs and the `Soa` version runs in 22µs, a 6x improvement.\n\n## Comparison\n\n### [`soa_derive`](https://docs.rs/soa_derive/latest/soa_derive/)\n\n`soa_derive` makes each field its own `Vec`. Because of this, each field's\nlength, capacity, and allocation are managed separately. In contrast, soa-rs\nmanages a single allocation for each `Soa`. `soa_derive` also generates a new\ncollection type for every struct, whereas soa-rs generates a minimal, low-level\ninterface that the generic `Soa` type uses for its implementation. This provides\nmore type system flexibility, less code generation, and better documentation.\n\n### [`soa-vec`](https://docs.rs/soa-vec/latest/soa_vec/)\n\nWhereas `soa-vec` only compiles on nightly, soa-rs also compiles on stable.\nRather than using derive macros, `soa-vec` instead uses macros to generate\neight static copies of their SoA type with fixed tuple sizes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftim-harding%2Fsoa-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftim-harding%2Fsoa-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftim-harding%2Fsoa-rs/lists"}