{"id":16916192,"url":"https://github.com/zesterer/ffd","last_synced_at":"2025-03-22T10:32:42.589Z","repository":{"id":231902402,"uuid":"782979309","full_name":"zesterer/ffd","owner":"zesterer","description":"Fast Function Dispatch: Improving the performance of Rust's dynamic function calls","archived":false,"fork":false,"pushed_at":"2024-10-19T00:15:00.000Z","size":5,"stargazers_count":32,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-18T10:45:48.596Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://docs.rs/ffd/0.1.0-alpha.0/ffd/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zesterer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-04-06T15:41:07.000Z","updated_at":"2024-11-06T22:06:27.000Z","dependencies_parsed_at":"2024-10-28T13:16:01.088Z","dependency_job_id":"e591d043-7fed-45d0-9c7c-eb167d29bbbd","html_url":"https://github.com/zesterer/ffd","commit_stats":{"total_commits":6,"total_committers":2,"mean_commits":3.0,"dds":"0.16666666666666663","last_synced_commit":"7912d4423fa7140650248144ecc6df5ffdac1b2a"},"previous_names":["zesterer/ffd"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zesterer%2Fffd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zesterer%2Fffd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zesterer%2Fffd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zesterer%2Fffd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zesterer","download_url":"https://codeload.github.com/zesterer/ffd/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244944401,"owners_count":20536290,"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":[],"created_at":"2024-10-13T19:25:35.898Z","updated_at":"2025-03-22T10:32:42.345Z","avatar_url":"https://github.com/zesterer.png","language":"Rust","readme":"# Fast Function Dispatch: Improving the performance of Rust's dynamic function calls\n\n[![crates.io](https://img.shields.io/crates/v/ffd.svg)](https://crates.io/crates/ffd)\n[![crates.io](https://docs.rs/ffd/badge.svg)](https://docs.rs/ffd)\n[![License](https://img.shields.io/crates/l/ffd.svg)](https://github.com/zesterer/ffd)\n[![actions-badge](https://github.com/zesterer/ffd/workflows/Rust/badge.svg?branch=master)](https://github.com/zesterer/ffd/actions)\n\nA safe, pragmatic toolkit for high-performance virtual function calls.\n\nThis library provides alternatives to types like `Box\u003cdyn Fn(...) -\u003e _\u003e` that are more performant in a range of\nscenarios.\n\n## Feature flags\n\n- `nightly`: Implements `Fn` for `Func`, as well as allowing `Func::new` to accept multi-argument closures\n\n## Why?\n\nYou'll often hear it said that Rust is packed full of zero-cost abstractions.\n\nIn spirit, this is often true! Many of Rust's fancy features do indeed compile down to machine code that's close enough\nto what one might write by hand in a 'low-level' language like C that the differences are fairly meaningless.\n\nSadly, an exception to this rule is *function dispatch*.\n\nRust's strategy, upon seeing a trait like the following, and a corresponding `dyn` coercion, is to generate a\n[vtable](https://doc.rust-lang.org/nomicon/exotic-sizes.html?highlight=vtable#dynamically-sized-types-dsts).\n\n```ignore\ntrait MyTrait {\n    fn do_something(\u0026self);\n    fn do_something_else(\u0026self, x: i32);\n}\n\nstruct MyStruct { a: i32 }\n\nimpl MyTrait for MyStruct {\n    fn do_something(\u0026self) { println!(\"{}\", self.a); }\n    fn do_something_else(\u0026self, a: i32) { println!(\"{a}\"); }\n}\n```\n\nThe vtable might look something like this:\n\n```ignore\nstruct MyTraitVtable {\n    // `*const ()` represents the `\u0026self` argument of `do_something`\n    do_something: fn(*const ()),\n    do_something_else: fn(*const (), i32),\n}\n\nstatic MYSTRUCT_MYTRAIT_VTABLE: MyTraitVtable = MyTraitVtable {\n    do_something: MyStruct::do_something as fn(_),\n    do_something_else: MyStruct::do_something_else as fn(_, _),\n};\n```\n\nBy and large, this is a reasonable strategy: when the compiler sees `\u0026dyn MyTrait`, it'll internally represent this as\nwide pointer, somewhat akin to the following tuple:\n\n```ignore\n(*const (), *const MyTraitVtable)\n```\n\nThe first field represents the pointer to the data, `\u0026self`. The second field is the vtable, allowing us to look up\nmethods at runtime.\n\nWhen calling a method on the trait object, the compiler will generate code that first dereferences the vtable pointer to\nfind the vtable, and then selects the field corresponding to the method being invoked. This field is a function pointer:\nso we can now call this function pointer using the data pointer as its argument.\n\nThis works brilliantly for most traits.\n\nSadly, Rust also uses the same strategy for dispatching dynamic function calls: the\n[`Fn` traits](https://doc.rust-lang.org/std/ops/trait.Fn.html) appear, to Rust, like any other trait. This is\nunnecessarily inefficient! The `Fn` trait only has one very commonly invoked method, `Fn::call`: why should we need to\nperform *double indirection*, jumping through two locations in memory, when we could just carry the `Fn::call` function\npointer around directly as the pointer metadata? Worse still, this double-indirection can severely pessimise the code\ngeneration of both the caller and callee, trashing register state and requiring unnecessary stack operations.\n\n99% of the time, this relatively tiny inefficiency is of no consequence. However, there\n[are circumstances](https://en.wikipedia.org/wiki/Threaded_code) in which this overhead really starts to matter, and it\nis for those circumstances that this library exists.\n\n## Planned features\n\n- Covering concurrency use-cases: `Send` and `Sync` functions\n- Covering more of the `Fn` traits: `FnMut`, `FnOnce`, etc.\n- Different representation strategies: drop function in pointer metadata instead?\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzesterer%2Fffd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzesterer%2Fffd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzesterer%2Fffd/lists"}