From 16727324316b5d9dbbed1abb65239bc9a64c91ac Mon Sep 17 00:00:00 2001 From: zxq5 Date: Tue, 3 Mar 2026 17:19:07 +0000 Subject: [PATCH] faster memory impl --- dsa-emu/.gitignore | 1 + dsa-emu/Cargo.lock | 571 ++++++++++++++++++++++++ dsa-emu/Cargo.toml | 12 + dsa-emu/benches/bench_mainstore.rs | 234 ++++++++++ dsa-emu/src/config/dsa.mmap.default.ron | 41 ++ dsa-emu/src/lib.rs | 12 + dsa-emu/src/main.rs | 39 +- dsa-emu/src/memory/mainstore.rs | 136 +++++- dsa-emu/src/memory/mod.rs | 39 +- dsa-emu/src/processor/processor.rs | 36 +- 10 files changed, 1088 insertions(+), 33 deletions(-) create mode 100644 dsa-emu/benches/bench_mainstore.rs create mode 100644 dsa-emu/src/config/dsa.mmap.default.ron create mode 100644 dsa-emu/src/lib.rs diff --git a/dsa-emu/.gitignore b/dsa-emu/.gitignore index ea8c4bf..96ef6c0 100644 --- a/dsa-emu/.gitignore +++ b/dsa-emu/.gitignore @@ -1 +1,2 @@ /target +Cargo.lock diff --git a/dsa-emu/Cargo.lock b/dsa-emu/Cargo.lock index 4af1e7b..3f728be 100644 --- a/dsa-emu/Cargo.lock +++ b/dsa-emu/Cargo.lock @@ -2,6 +2,71 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + [[package]] name = "arc-swap" version = "1.8.2" @@ -11,6 +76,12 @@ dependencies = [ "rustversion", ] +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "bitflags" version = "2.11.0" @@ -20,22 +91,188 @@ dependencies = [ "serde_core", ] +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "dsa-emu" version = "0.1.0" dependencies = [ "arc-swap", + "clap", + "criterion", "fxhash", "ron", "serde", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "fxhash" version = "0.2.1" @@ -45,12 +282,138 @@ dependencies = [ "byteorder", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -69,6 +432,55 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + [[package]] name = "ron" version = "0.12.0" @@ -89,6 +501,15 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "serde" version = "1.0.228" @@ -119,6 +540,25 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.117" @@ -130,6 +570,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "typeid" version = "1.0.3" @@ -141,3 +591,124 @@ name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/dsa-emu/Cargo.toml b/dsa-emu/Cargo.toml index f1cbc85..25ba853 100644 --- a/dsa-emu/Cargo.toml +++ b/dsa-emu/Cargo.toml @@ -3,8 +3,20 @@ name = "dsa-emu" version = "0.1.0" edition = "2024" +[lib] +name = "dsa" +path = "src/lib.rs" + +[dev-dependencies] +criterion = { version = "0.5.0", features = ["html_reports"] } + [dependencies] arc-swap = "1.8.2" +clap = { version = "4.5.60", features = ["derive"] } fxhash = "0.2.1" ron = "0.12.0" serde = { version = "1.0.228", features = ["derive"] } + +[[bench]] +name = "bench_mainstore" +harness = false diff --git a/dsa-emu/benches/bench_mainstore.rs b/dsa-emu/benches/bench_mainstore.rs new file mode 100644 index 0000000..2e25eb9 --- /dev/null +++ b/dsa-emu/benches/bench_mainstore.rs @@ -0,0 +1,234 @@ +use criterion::{ + BenchmarkGroup, BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main, + measurement::WallTime, +}; +use std::time::Duration; + +use dsa::{FastStore, MainStore, RandomAccessMemory}; + +// ── reproduce the trait and PhysAddr here, or import from your crate ───────── +type PhysAddr = u32; + +// ── address generators ──────────────────────────────────────────────────────── + +/// Cycles through a small set of addresses — stays hot in L1/L2. +/// Simulates a tight inner loop hitting the same working set repeatedly. +fn sequential_addrs(n: usize, max_addr: u32) -> Vec { + (0..n).map(|i| ((i as u32 * 4) & (max_addr - 1))).collect() +} + +/// LCG pseudo-random addresses across the full address space. +/// Simulates worst-case cache behaviour. +fn random_addrs(n: usize, max_addr: u32) -> Vec { + let mut addrs = Vec::with_capacity(n); + let mut x: u32 = 0xdeadbeef; + for _ in 0..n { + x = x.wrapping_mul(1664525).wrapping_add(1013904223); + addrs.push((x & (max_addr - 1)) & !3); + } + addrs +} + +/// Walks addresses page by page — tests page-boundary crossing behaviour. +fn page_stride_addrs(n: usize) -> Vec { + (0..n).map(|i| (i as u32 * 4096) & 0x00FF_FFFF).collect() +} + +// ── generic benchmark functions ─────────────────────────────────────────────── + +fn bench_read_byte( + group: &mut BenchmarkGroup, + name: &str, + mem: &M, + addrs: &[PhysAddr], +) { + group.throughput(Throughput::Elements(addrs.len() as u64)); + group.bench_function(name, |b| { + b.iter(|| { + let mut sum: u32 = 0; + for &addr in addrs { + sum = sum.wrapping_add(mem.read_byte(black_box(addr)) as u32); + } + black_box(sum) + }) + }); +} + +fn bench_write_byte( + group: &mut BenchmarkGroup, + name: &str, + mem: &mut M, + addrs: &[PhysAddr], +) { + group.throughput(Throughput::Elements(addrs.len() as u64)); + group.bench_function(name, |b| { + b.iter(|| { + for (i, &addr) in addrs.iter().enumerate() { + mem.write_byte(black_box(addr), black_box(i as u8)); + } + }) + }); +} + +fn bench_read_word( + group: &mut BenchmarkGroup, + name: &str, + mem: &M, + addrs: &[PhysAddr], +) { + group.throughput(Throughput::Elements(addrs.len() as u64)); + group.bench_function(name, |b| { + b.iter(|| { + let mut sum: u32 = 0; + for &addr in addrs { + sum = sum.wrapping_add(mem.read_word(black_box(addr))); + } + black_box(sum) + }) + }); +} + +fn bench_write_word( + group: &mut BenchmarkGroup, + name: &str, + mem: &mut M, + addrs: &[PhysAddr], +) { + group.throughput(Throughput::Elements(addrs.len() as u64)); + group.bench_function(name, |b| { + b.iter(|| { + for (i, &addr) in addrs.iter().enumerate() { + mem.write_word(black_box(addr), black_box(i as u32)); + } + }) + }); +} + +fn bench_read_page( + group: &mut BenchmarkGroup, + name: &str, + mem: &M, + addrs: &[PhysAddr], +) { + group.throughput(Throughput::Bytes(addrs.len() as u64 * 4096)); + group.bench_function(name, |b| { + b.iter(|| { + let mut sum: u8 = 0; + for &addr in addrs { + let page = mem.read_page(black_box(addr)); + sum = sum.wrapping_add(page[0]); + } + black_box(sum) + }) + }); +} + +fn bench_write_page( + group: &mut BenchmarkGroup, + name: &str, + mem: &mut M, + addrs: &[PhysAddr], +) { + let page_data = [0xABu8; 4096]; + group.throughput(Throughput::Bytes(addrs.len() as u64 * 4096)); + group.bench_function(name, |b| { + b.iter(|| { + for &addr in addrs { + mem.write_page(black_box(addr), black_box(&page_data)); + } + }) + }); +} + +// ── run all access patterns for a given implementation ──────────────────────── + +fn bench_implementation( + c: &mut Criterion, + impl_name: &str, + mut mem: M, + max_addr: u32, +) { + const N: usize = 64; + + let seq_addrs = sequential_addrs(N, max_addr); + let rand_addrs = random_addrs(N, max_addr); + let page_stride = page_stride_addrs(N); + + // ── read_byte ──────────────────────────────────────────────────────────── + { + let mut g = c.benchmark_group(format!("{}/read_byte", impl_name)); + g.measurement_time(Duration::from_secs(3)); + bench_read_byte(&mut g, "sequential", &mem, &seq_addrs); + bench_read_byte(&mut g, "random", &mem, &rand_addrs); + g.finish(); + } + + // ── write_byte ─────────────────────────────────────────────────────────── + { + let mut g = c.benchmark_group(format!("{}/write_byte", impl_name)); + g.measurement_time(Duration::from_secs(3)); + bench_write_byte(&mut g, "sequential", &mut mem, &seq_addrs); + bench_write_byte(&mut g, "random", &mut mem, &rand_addrs); + g.finish(); + } + + // ── read_word ──────────────────────────────────────────────────────────── + { + let mut g = c.benchmark_group(format!("{}/read_word", impl_name)); + g.measurement_time(Duration::from_secs(3)); + bench_read_word(&mut g, "sequential", &mem, &seq_addrs); + bench_read_word(&mut g, "random", &mem, &rand_addrs); + g.finish(); + } + + // ── write_word ─────────────────────────────────────────────────────────── + { + let mut g = c.benchmark_group(format!("{}/write_word", impl_name)); + g.measurement_time(Duration::from_secs(3)); + bench_write_word(&mut g, "sequential", &mut mem, &seq_addrs); + bench_write_word(&mut g, "random", &mut mem, &rand_addrs); + g.finish(); + } + + // ── read_page ──────────────────────────────────────────────────────────── + { + let mut g = c.benchmark_group(format!("{}/read_page", impl_name)); + g.measurement_time(Duration::from_secs(3)); + bench_read_page(&mut g, "sequential", &mem, &seq_addrs); + bench_read_page(&mut g, "random", &mem, &rand_addrs); + bench_read_page(&mut g, "page_stride", &mem, &page_stride); + g.finish(); + } + + // ── write_page ─────────────────────────────────────────────────────────── + { + let mut g = c.benchmark_group(format!("{}/write_page", impl_name)); + g.measurement_time(Duration::from_secs(3)); + bench_write_page(&mut g, "sequential", &mut mem, &seq_addrs); + bench_write_page(&mut g, "random", &mut mem, &rand_addrs); + bench_write_page(&mut g, "page_stride", &mut mem, &page_stride); + g.finish(); + } +} + +// ── wire up your implementations here ──────────────────────────────────────── +// +// Replace these stubs with your actual types. Each call to bench_implementation +// runs the full suite and labels it separately in the HTML report. +// +// Example: +// fn benchmarks(c: &mut Criterion) { +// bench_implementation(c, "MainStore", MainStore::new(), 0x00FF_FFFF); +// bench_implementation(c, "FxHashMap", HashMapMem::new(), 0x00FF_FFFF); +// } +// +fn benchmarks(c: &mut Criterion) { + // TODO: replace with your implementations + bench_implementation(c, "MainStore", MainStore::new(), 0x00FF_FFFF); + bench_implementation(c, "FastStore", FastStore::new(), 0x00FF_FFFF); + + let _ = c; +} + +criterion_group!(benches, benchmarks); +criterion_main!(benches); diff --git a/dsa-emu/src/config/dsa.mmap.default.ron b/dsa-emu/src/config/dsa.mmap.default.ron new file mode 100644 index 0000000..a589872 --- /dev/null +++ b/dsa-emu/src/config/dsa.mmap.default.ron @@ -0,0 +1,41 @@ +MemoryMap( + table_addr: 0x1000, + regions: [ + // 0000-1000 = reserved + Region( + base: 0x0, + size: 0x1000, + region_type: Reserved + ), + // 1000-4000 = bootloader + Region( + base: 0x1000, + size: 0x3000, + region_type: Bootloader + ), + // 5000-0FFFFFFF = MMIO + Region( + base: 0x5000, + size: 0x0FFF_FFFF, + region_type: MMIO + ), + // 10000000-BFFFFFFF = userspace + Region( + base: 0x1000_0000, + size: 0xBFFF_FFFF, + region_type: Usable + ), + // C0000000-C0FFFFFF = kernel page tables + Region( + base: 0xC000_0000, + size: 0x100_0000, + region_type: KernelTables + ), + // C1000000-FFFFFFFF = kernel + Region( + base: 0xC100_0000, + size: 0x3EFF_FFFF, + region_type: Kernel + ) + ] +) diff --git a/dsa-emu/src/lib.rs b/dsa-emu/src/lib.rs new file mode 100644 index 0000000..db98b6a --- /dev/null +++ b/dsa-emu/src/lib.rs @@ -0,0 +1,12 @@ +#![feature(likely_unlikely)] +#![feature(ptr_as_ref_unchecked)] + +mod memory; +mod processor; + +pub use { + memory::MemoryMap, + memory::mainstore::{FastStore, MainStore, RandomAccessMemory}, + processor::processor::Emulator, + processor::state::SharedState, +}; diff --git a/dsa-emu/src/main.rs b/dsa-emu/src/main.rs index e3e32a5..94e1662 100644 --- a/dsa-emu/src/main.rs +++ b/dsa-emu/src/main.rs @@ -1,5 +1,8 @@ use arc_swap::ArcSwap; +use clap::Parser; use std::{ + fs, + path::PathBuf, sync::{ Arc, atomic::{AtomicBool, AtomicU8}, @@ -8,13 +11,20 @@ use std::{ time::Duration, }; -use crate::processor::{processor::Emulator, state::SharedState}; - -mod memory; -mod processor; +use dsa::{Emulator, FastStore, MainStore, MemoryMap, SharedState}; fn main() { - let mut emulator = Emulator::new(); + let args = DsaArgs::parse(); + + let mut emulator = Emulator::new(FastStore::new()); + + let mmap = match args.get_memory_map() { + Some(m) => m, + None => MemoryMap::default(), + }; + + emulator.apply_memory_map(mmap); + let state = emulator.state_handle(); let runner = thread::spawn(move || emulator.run()); let observer = thread::spawn(|| observe(state)); @@ -30,3 +40,22 @@ fn observe(state: Arc) { println!("GP Registers: {:?}", state.gp_registers); } } + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct DsaArgs { + /// Memory map to use on boot. + /// Overrides default value. + #[arg(long = "mmap")] + memory_map: Option, +} + +impl DsaArgs { + pub fn get_memory_map(&self) -> Option { + self.memory_map.as_ref().and_then(|m| { + fs::read_to_string(m) + .ok() + .and_then(|map| ron::from_str(&map).ok()) + }) + } +} diff --git a/dsa-emu/src/memory/mainstore.rs b/dsa-emu/src/memory/mainstore.rs index 3981d29..3d8a844 100644 --- a/dsa-emu/src/memory/mainstore.rs +++ b/dsa-emu/src/memory/mainstore.rs @@ -1,9 +1,27 @@ use fxhash::FxHashMap; use crate::memory::PhysAddr; +use std::{ + alloc::{Layout, alloc_zeroed}, + hint::likely, +}; type Page = [u8; 4096]; +pub trait RandomAccessMemory { + fn read_byte(&self, addr: PhysAddr) -> u8; + + fn write_byte(&mut self, addr: PhysAddr, value: u8); + + fn read_word(&self, addr: PhysAddr) -> u32; + + fn write_word(&mut self, addr: PhysAddr, value: u32); + + fn read_page(&self, addr: PhysAddr) -> &[u8; 4096]; + + fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]); +} + pub struct MainStore { pages: FxHashMap, } @@ -19,42 +37,130 @@ impl MainStore { const fn segment_addr(addr: PhysAddr) -> (PhysAddr, usize) { (addr & !(0xFFF), (addr & 0xFFF) as usize) } +} - #[inline] - pub fn read_byte(&self, addr: PhysAddr) -> u8 { +impl RandomAccessMemory for MainStore { + #[inline(always)] + fn read_byte(&self, addr: PhysAddr) -> u8 { let (page, offset) = Self::segment_addr(addr); self.pages.get(&page).map(|p| p[offset]).unwrap_or(0) } - #[inline] - pub fn read_word(&self, addr: PhysAddr) -> u32 { + #[inline(always)] + fn read_word(&self, addr: PhysAddr) -> u32 { let (page, offset) = Self::segment_addr(addr); + debug_assert_eq!(offset % 4, 0); let page = self.pages.get(&page).unwrap_or(&[0; 4096]); u32::from_be_bytes(page[offset..=offset + 3].try_into().unwrap()) } - #[inline] - pub fn read_page(&self, addr: PhysAddr) -> &[u8] { + #[inline(always)] + fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] { + debug_assert_eq!(addr % 0x1000, 0); self.pages.get(&addr).unwrap_or(&[0; 4096]) } - #[inline] - pub fn write_byte(&mut self, addr: PhysAddr, value: u8) { + #[inline(always)] + fn write_byte(&mut self, addr: PhysAddr, value: u8) { let (page, offset) = Self::segment_addr(addr); self.pages.entry(page).or_insert_with(|| [0; 4096])[offset] = value; } - #[inline] - pub fn write_word(&mut self, addr: PhysAddr, value: u32) { + #[inline(always)] + fn write_word(&mut self, addr: PhysAddr, value: u32) { let (page, offset) = Self::segment_addr(addr); + debug_assert_eq!(offset % 4, 0); let page = self.pages.entry(page).or_insert_with(|| [0; 4096]); page[offset..=offset + 3].copy_from_slice(&value.to_be_bytes()); } - #[inline] - pub fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) { - let (page, offset) = Self::segment_addr(addr); - let page = self.pages.entry(page).or_insert_with(|| [0; 4096]); - page[offset..offset + value.len()].copy_from_slice(value); + #[inline(always)] + fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) { + debug_assert_eq!(addr % 0x1000, 0); + let page = self.pages.entry(addr).or_insert_with(|| [0; 4096]); + page.copy_from_slice(value); + } +} + +const NUM_PAGES: usize = 2 << 20; + +pub struct FastStore { + pages: Box<[*mut Page; NUM_PAGES]>, +} + +unsafe impl Send for FastStore {} +impl FastStore { + pub fn new() -> Self { + let pages = vec![0 as *mut Page; 2 << 20] + .into_boxed_slice() + .try_into() + .unwrap(); + Self { pages } + } + + fn alloc(&mut self) -> *mut Page { + let layout = Layout::from_size_align(4096, 4096).unwrap(); + unsafe { alloc_zeroed(layout) as *mut Page } + } +} + +impl RandomAccessMemory for FastStore { + fn read_word(&self, addr: PhysAddr) -> u32 { + let page = self.pages[(addr >> 12) as usize]; + if likely(!page.is_null()) { + let offset = (addr & 0xFFF) as usize; + return unsafe { (page.add(offset) as *const u32).read() }; + } + + // Slow path: MMIO, fault, etc — never inlined + // Since we're only reading, we can safely return 0 + return 0; + } + + fn read_byte(&self, addr: PhysAddr) -> u8 { + let page = self.pages[(addr >> 12) as usize]; + if likely(!page.is_null()) { + let offset = (addr & 0xFFF) as usize; + return unsafe { (page.add(offset) as *const u8).read() }; + } + + return 0; + } + + fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] { + let page = self.pages[(addr >> 12) as usize]; + if likely(!page.is_null()) { + let offset = (addr & 0xFFF) as usize; + return unsafe { &*(page.add(offset) as *const [u8; 4096]) }; + } + + return &[0; 4096]; + } + + fn write_byte(&mut self, addr: PhysAddr, value: u8) { + let page = self.pages[(addr >> 12) as usize]; + + if likely(!page.is_null()) { + let offset = (addr & 0xFFF) as usize; + unsafe { (page.add(offset) as *mut u8).write(value) }; + } + } + + fn write_word(&mut self, addr: PhysAddr, value: u32) { + let page = self.pages[(addr >> 12) as usize]; + + if likely(!page.is_null()) { + let offset = (addr & 0xFFF) as usize; + unsafe { (page.add(offset) as *mut u32).write(value) }; + } + } + + fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) { + let page = self.pages[(addr >> 12) as usize]; + + if likely(!page.is_null()) { + let offset = (addr & 0xFFF) as usize; + unsafe { (page.add(offset) as *mut [u8; 4096]).write(*value) }; + } } } diff --git a/dsa-emu/src/memory/mod.rs b/dsa-emu/src/memory/mod.rs index ccd5e15..797ae3a 100644 --- a/dsa-emu/src/memory/mod.rs +++ b/dsa-emu/src/memory/mod.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::memory::mmu::MMU; +use crate::{ + memory::{mainstore::RandomAccessMemory, mmu::MMU}, + processor::processor::Emulator, +}; mod cache; pub mod mainstore; @@ -15,7 +18,7 @@ pub enum FaultInfo { } #[repr(u32)] -#[derive(Serialize, Deserialize, Clone, Copy)] +#[derive(Serialize, Deserialize, Clone, Copy, Debug)] enum RegionType { Reserved, Bootloader, @@ -25,14 +28,18 @@ enum RegionType { MMIO, } -#[derive(Serialize, Deserialize, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct Region { base: PhysAddr, - size: usize, + size: u32, region_type: RegionType, + + // flags for the emulator + #[serde(default)] + identity_map_on_boot: bool, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct MemoryMap { table_addr: PhysAddr, pub regions: Vec, @@ -48,9 +55,27 @@ impl MemoryMap { } } + pub fn apply(&self, cpu: &mut Emulator) { + for reg in &self.regions { + if reg.identity_map_on_boot { + Self::identity_map(cpu.mmu_mut(), reg); + } + } + + let mem = cpu.memory_mut(); + let mut offset = self.table_addr; + for region in &self.regions { + println!("Wrote entry: {region:?} to page table"); + mem.write_word(offset, region.base); + mem.write_word(offset + 4, region.size); + mem.write_word(offset + 8, region.region_type as u32); + offset += 12; + } + } + pub fn identity_map(mmu: &mut MMU, region: &Region) { let first_page = region.base >> 12; - let page_count = (region.size >> 12) as u32; + let page_count = (region.size >> 12); for page in (first_page..first_page + page_count).map(|p| p << 12) { mmu.create_mapping(page, page); @@ -60,6 +85,6 @@ impl MemoryMap { impl Default for MemoryMap { fn default() -> Self { - ron::from_str(include_str!("../../config/dsa.mmap.ron")).unwrap() + ron::from_str(include_str!("../config/dsa.mmap.default.ron")).unwrap() } } diff --git a/dsa-emu/src/processor/processor.rs b/dsa-emu/src/processor/processor.rs index fb76771..37b04ee 100644 --- a/dsa-emu/src/processor/processor.rs +++ b/dsa-emu/src/processor/processor.rs @@ -1,28 +1,38 @@ use std::{sync::Arc, thread, time::Duration}; use crate::{ - memory::{FaultInfo, MemoryMap, mainstore::MainStore, mmu::MMU}, + memory::{ + FaultInfo, MemoryMap, + mainstore::{MainStore, RandomAccessMemory}, + mmu::MMU, + }, processor::{ interrupts::Interrupt, state::{ProcessorSnapshot, SharedState}, }, }; -pub struct Emulator { +pub struct Emulator { internal_state: ProcessorSnapshot, shared_state: Arc, + // memory mmu: MMU, - mainstore: MainStore, + mainstore: Mem, + + // optionals - not necessarily set on boot. + memory_map: Option, } -impl Emulator { - pub fn new() -> Self { +unsafe impl Send for Emulator {} +impl Emulator { + pub fn new(mem: Mem) -> Self { Self { internal_state: ProcessorSnapshot::default(), shared_state: Arc::new(SharedState::new()), mmu: MMU::new(), - mainstore: MainStore::new(), + mainstore: mem, + memory_map: None, } } @@ -30,6 +40,20 @@ impl Emulator { self.shared_state.clone() } + pub fn mmu_mut(&mut self) -> &mut MMU { + &mut self.mmu + } + + pub fn memory_mut(&mut self) -> &mut impl RandomAccessMemory { + &mut self.mainstore + } + + pub fn apply_memory_map(&mut self, map: MemoryMap) -> &mut Self { + map.apply(self); + self.memory_map = Some(map); + self + } + pub fn run(&mut self) { let mut clock = 0;