faster memory impl

This commit is contained in:
2026-03-03 17:19:07 +00:00
parent ebb9489410
commit 1672732431
10 changed files with 1088 additions and 33 deletions
+1
View File
@@ -1 +1,2 @@
/target /target
Cargo.lock
+571
View File
@@ -2,6 +2,71 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 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]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "1.8.2" version = "1.8.2"
@@ -11,6 +76,12 @@ dependencies = [
"rustversion", "rustversion",
] ]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.11.0" version = "2.11.0"
@@ -20,22 +91,188 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "bumpalo"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.5.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 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]] [[package]]
name = "dsa-emu" name = "dsa-emu"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"clap",
"criterion",
"fxhash", "fxhash",
"ron", "ron",
"serde", "serde",
] ]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]] [[package]]
name = "fxhash" name = "fxhash"
version = "0.2.1" version = "0.2.1"
@@ -45,12 +282,138 @@ dependencies = [
"byteorder", "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]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.3" version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.106" version = "1.0.106"
@@ -69,6 +432,55 @@ dependencies = [
"proc-macro2", "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]] [[package]]
name = "ron" name = "ron"
version = "0.12.0" version = "0.12.0"
@@ -89,6 +501,15 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 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]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.228"
@@ -119,6 +540,25 @@ dependencies = [
"syn", "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]] [[package]]
name = "syn" name = "syn"
version = "2.0.117" version = "2.0.117"
@@ -130,6 +570,16 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "typeid" name = "typeid"
version = "1.0.3" version = "1.0.3"
@@ -141,3 +591,124 @@ name = "unicode-ident"
version = "1.0.24" version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 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"
+12
View File
@@ -3,8 +3,20 @@ name = "dsa-emu"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[lib]
name = "dsa"
path = "src/lib.rs"
[dev-dependencies]
criterion = { version = "0.5.0", features = ["html_reports"] }
[dependencies] [dependencies]
arc-swap = "1.8.2" arc-swap = "1.8.2"
clap = { version = "4.5.60", features = ["derive"] }
fxhash = "0.2.1" fxhash = "0.2.1"
ron = "0.12.0" ron = "0.12.0"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
[[bench]]
name = "bench_mainstore"
harness = false
+234
View File
@@ -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<PhysAddr> {
(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<PhysAddr> {
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<PhysAddr> {
(0..n).map(|i| (i as u32 * 4096) & 0x00FF_FFFF).collect()
}
// ── generic benchmark functions ───────────────────────────────────────────────
fn bench_read_byte<M: RandomAccessMemory>(
group: &mut BenchmarkGroup<WallTime>,
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<M: RandomAccessMemory>(
group: &mut BenchmarkGroup<WallTime>,
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<M: RandomAccessMemory>(
group: &mut BenchmarkGroup<WallTime>,
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<M: RandomAccessMemory>(
group: &mut BenchmarkGroup<WallTime>,
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<M: RandomAccessMemory>(
group: &mut BenchmarkGroup<WallTime>,
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<M: RandomAccessMemory>(
group: &mut BenchmarkGroup<WallTime>,
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<M: RandomAccessMemory>(
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);
+41
View File
@@ -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
)
]
)
+12
View File
@@ -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,
};
+34 -5
View File
@@ -1,5 +1,8 @@
use arc_swap::ArcSwap; use arc_swap::ArcSwap;
use clap::Parser;
use std::{ use std::{
fs,
path::PathBuf,
sync::{ sync::{
Arc, Arc,
atomic::{AtomicBool, AtomicU8}, atomic::{AtomicBool, AtomicU8},
@@ -8,13 +11,20 @@ use std::{
time::Duration, time::Duration,
}; };
use crate::processor::{processor::Emulator, state::SharedState}; use dsa::{Emulator, FastStore, MainStore, MemoryMap, SharedState};
mod memory;
mod processor;
fn main() { 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 state = emulator.state_handle();
let runner = thread::spawn(move || emulator.run()); let runner = thread::spawn(move || emulator.run());
let observer = thread::spawn(|| observe(state)); let observer = thread::spawn(|| observe(state));
@@ -30,3 +40,22 @@ fn observe(state: Arc<SharedState>) {
println!("GP Registers: {:?}", state.gp_registers); 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<PathBuf>,
}
impl DsaArgs {
pub fn get_memory_map(&self) -> Option<MemoryMap> {
self.memory_map.as_ref().and_then(|m| {
fs::read_to_string(m)
.ok()
.and_then(|map| ron::from_str(&map).ok())
})
}
}
+121 -15
View File
@@ -1,9 +1,27 @@
use fxhash::FxHashMap; use fxhash::FxHashMap;
use crate::memory::PhysAddr; use crate::memory::PhysAddr;
use std::{
alloc::{Layout, alloc_zeroed},
hint::likely,
};
type Page = [u8; 4096]; 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 { pub struct MainStore {
pages: FxHashMap<PhysAddr, Page>, pages: FxHashMap<PhysAddr, Page>,
} }
@@ -19,42 +37,130 @@ impl MainStore {
const fn segment_addr(addr: PhysAddr) -> (PhysAddr, usize) { const fn segment_addr(addr: PhysAddr) -> (PhysAddr, usize) {
(addr & !(0xFFF), (addr & 0xFFF) as usize) (addr & !(0xFFF), (addr & 0xFFF) as usize)
} }
}
#[inline] impl RandomAccessMemory for MainStore {
pub fn read_byte(&self, addr: PhysAddr) -> u8 { #[inline(always)]
fn read_byte(&self, addr: PhysAddr) -> u8 {
let (page, offset) = Self::segment_addr(addr); let (page, offset) = Self::segment_addr(addr);
self.pages.get(&page).map(|p| p[offset]).unwrap_or(0) self.pages.get(&page).map(|p| p[offset]).unwrap_or(0)
} }
#[inline] #[inline(always)]
pub fn read_word(&self, addr: PhysAddr) -> u32 { fn read_word(&self, addr: PhysAddr) -> u32 {
let (page, offset) = Self::segment_addr(addr); let (page, offset) = Self::segment_addr(addr);
debug_assert_eq!(offset % 4, 0);
let page = self.pages.get(&page).unwrap_or(&[0; 4096]); let page = self.pages.get(&page).unwrap_or(&[0; 4096]);
u32::from_be_bytes(page[offset..=offset + 3].try_into().unwrap()) u32::from_be_bytes(page[offset..=offset + 3].try_into().unwrap())
} }
#[inline] #[inline(always)]
pub fn read_page(&self, addr: PhysAddr) -> &[u8] { fn read_page(&self, addr: PhysAddr) -> &[u8; 4096] {
debug_assert_eq!(addr % 0x1000, 0);
self.pages.get(&addr).unwrap_or(&[0; 4096]) self.pages.get(&addr).unwrap_or(&[0; 4096])
} }
#[inline] #[inline(always)]
pub fn write_byte(&mut self, addr: PhysAddr, value: u8) { fn write_byte(&mut self, addr: PhysAddr, value: u8) {
let (page, offset) = Self::segment_addr(addr); let (page, offset) = Self::segment_addr(addr);
self.pages.entry(page).or_insert_with(|| [0; 4096])[offset] = value; self.pages.entry(page).or_insert_with(|| [0; 4096])[offset] = value;
} }
#[inline] #[inline(always)]
pub fn write_word(&mut self, addr: PhysAddr, value: u32) { fn write_word(&mut self, addr: PhysAddr, value: u32) {
let (page, offset) = Self::segment_addr(addr); let (page, offset) = Self::segment_addr(addr);
debug_assert_eq!(offset % 4, 0);
let page = self.pages.entry(page).or_insert_with(|| [0; 4096]); let page = self.pages.entry(page).or_insert_with(|| [0; 4096]);
page[offset..=offset + 3].copy_from_slice(&value.to_be_bytes()); page[offset..=offset + 3].copy_from_slice(&value.to_be_bytes());
} }
#[inline] #[inline(always)]
pub fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) { fn write_page(&mut self, addr: PhysAddr, value: &[u8; 4096]) {
let (page, offset) = Self::segment_addr(addr); debug_assert_eq!(addr % 0x1000, 0);
let page = self.pages.entry(page).or_insert_with(|| [0; 4096]); let page = self.pages.entry(addr).or_insert_with(|| [0; 4096]);
page[offset..offset + value.len()].copy_from_slice(value); 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) };
}
} }
} }
+32 -7
View File
@@ -1,6 +1,9 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::memory::mmu::MMU; use crate::{
memory::{mainstore::RandomAccessMemory, mmu::MMU},
processor::processor::Emulator,
};
mod cache; mod cache;
pub mod mainstore; pub mod mainstore;
@@ -15,7 +18,7 @@ pub enum FaultInfo {
} }
#[repr(u32)] #[repr(u32)]
#[derive(Serialize, Deserialize, Clone, Copy)] #[derive(Serialize, Deserialize, Clone, Copy, Debug)]
enum RegionType { enum RegionType {
Reserved, Reserved,
Bootloader, Bootloader,
@@ -25,14 +28,18 @@ enum RegionType {
MMIO, MMIO,
} }
#[derive(Serialize, Deserialize, Clone, Copy)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct Region { pub struct Region {
base: PhysAddr, base: PhysAddr,
size: usize, size: u32,
region_type: RegionType, 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 { pub struct MemoryMap {
table_addr: PhysAddr, table_addr: PhysAddr,
pub regions: Vec<Region>, pub regions: Vec<Region>,
@@ -48,9 +55,27 @@ impl MemoryMap {
} }
} }
pub fn apply(&self, cpu: &mut Emulator<impl RandomAccessMemory>) {
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) { pub fn identity_map(mmu: &mut MMU, region: &Region) {
let first_page = region.base >> 12; 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) { for page in (first_page..first_page + page_count).map(|p| p << 12) {
mmu.create_mapping(page, page); mmu.create_mapping(page, page);
@@ -60,6 +85,6 @@ impl MemoryMap {
impl Default for MemoryMap { impl Default for MemoryMap {
fn default() -> Self { fn default() -> Self {
ron::from_str(include_str!("../../config/dsa.mmap.ron")).unwrap() ron::from_str(include_str!("../config/dsa.mmap.default.ron")).unwrap()
} }
} }
+30 -6
View File
@@ -1,28 +1,38 @@
use std::{sync::Arc, thread, time::Duration}; use std::{sync::Arc, thread, time::Duration};
use crate::{ use crate::{
memory::{FaultInfo, MemoryMap, mainstore::MainStore, mmu::MMU}, memory::{
FaultInfo, MemoryMap,
mainstore::{MainStore, RandomAccessMemory},
mmu::MMU,
},
processor::{ processor::{
interrupts::Interrupt, interrupts::Interrupt,
state::{ProcessorSnapshot, SharedState}, state::{ProcessorSnapshot, SharedState},
}, },
}; };
pub struct Emulator { pub struct Emulator<Mem: RandomAccessMemory> {
internal_state: ProcessorSnapshot, internal_state: ProcessorSnapshot,
shared_state: Arc<SharedState>, shared_state: Arc<SharedState>,
// memory
mmu: MMU, mmu: MMU,
mainstore: MainStore, mainstore: Mem,
// optionals - not necessarily set on boot.
memory_map: Option<MemoryMap>,
} }
impl Emulator { unsafe impl<Mem: RandomAccessMemory> Send for Emulator<Mem> {}
pub fn new() -> Self { impl<Mem: RandomAccessMemory> Emulator<Mem> {
pub fn new(mem: Mem) -> Self {
Self { Self {
internal_state: ProcessorSnapshot::default(), internal_state: ProcessorSnapshot::default(),
shared_state: Arc::new(SharedState::new()), shared_state: Arc::new(SharedState::new()),
mmu: MMU::new(), mmu: MMU::new(),
mainstore: MainStore::new(), mainstore: mem,
memory_map: None,
} }
} }
@@ -30,6 +40,20 @@ impl Emulator {
self.shared_state.clone() 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) { pub fn run(&mut self) {
let mut clock = 0; let mut clock = 0;