-
Notifications
You must be signed in to change notification settings - Fork 694
Memory w/ 64-bit indexes #1325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I would be good to see the performance difference between 64-bit memories and 32-bit memories. Browsers rely heavily on VM page faults + clamping to enforce bounds checks. IIRC, disabling this was roughly a 10-15% performance regression. Unless there's an optimization I haven't seen yet that fixes this problem. This proposal, as is, forces engines to explicitly bounds check. I'd expect this to be a significant enough performance cliff to be a deal breaker for most apps... |
Good point. @titzer mentioned back in 2018 a potential trick that might help here:
|
|
Perhaps this is what folks meant by wasm64. I always thought it meant changing all of the indexes to be 64-bit. So for example, you could have > |
Ahh.. yes, most of these don't sound particularly urgent to me :) |
An alternative would be multi-level memory lookup, similarly to how page tables work on Linux. In a 64-bit address space, that would require at least three levels to keep the tables at each level at a manageable size. Two levels could still be implemented by only reserving (but not committing) the huge tables, but it would waste a lot of virtual address space. That scheme would require two additional loads for each memory operation. I could image that to still be faster than a dynamic branch. |
"wasm64" is indeed just a tool convention, and the main feature it depends on is linear memories with 64-bit indices. For 64-bit section sizes or data initializer sizes, many programs wouldn't need them, and adding them in the future wouldn't break compatibility. If we wanted tables with 64-bit indices for "wasm64" function pointers, that would be harder to add without breaking compatibility, so I opened WebAssembly/tool-conventions#136 to discuss function pointers. |
The proposal makes sense and indeed coincides with what I was vaguely assuming "wasm64" meant: basically, making 32-vs-64 a memory-by-memory choice by having it be part of the memory type. This has the added benefit of, with multi-memory, allowing a single module to use both 32-bit and 64-bit memories, which could be necessary for various fancy uses of wasm. |
Cool, I'm glad this is generally what folks were thinking of. :-) A few questions:
|
I think that for the time being, we should assume that the memory types have to match exactly. It's plausible that we will want to use different techniques to avoid OOB checks for 32-bit and 64-bit memories, and these techniques may require the memories to be allocated differently (eg with different buffer zones). I think that if we allow non-matching memory types we should justify it well. ArrayBuffers are limited by the range of Index values, [0 .. 2^53-1]. It does not seem to me to be much of a practical problem for a JS embedding to limit a wasm memory to this size. Would it be meaningful to allow 64-bit memories to be accessed with i32 addresses? Even on 64-bit architectures there's an advantage (instruction size, instruction count) to avoiding 64-bit data, and a toolchain that places static data in the low 4GB would benefit from the size and performance boost, modest though it may be. |
JS ArrayBuffers are spec'ed to be capped at That said, current x86_64 platforms only support 248 bits of virtual address space, and only very few computers in existence today have more than 235 bytes (32GB) of physical memory [1]. I think it might make sense to define a limit lower than 264 for Wasm, to reflect this reality. One could even go as far as starting with a max of 233, and expecting to bump it by one bit every few years as the need arises. Once the underlying types are spec'ed and implemented as i64/u64, bumping the limit should be trivially easy in terms of implementation effort. Or we could set it to, say, 248, and expect to leave it at that until that value becomes a limiting factor (=a couple of decades probably?). [1] https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam says "6.8% have more than 16GB", and doesn't even bother with a "more than 32GB" bucket. I know that >32GB machines exist (I'm typing this on one), but I also know that they are rare. |
That actually sounds like relatively a lot of complexity for a very small gain. Static addresses in an |
Per @jakobkummerow comments: There has to be a practical limit that is quite a bit lower. Commonly on Windows x64 (and several other common platforms I believe), the user-space limit on memory is 8TB (i.e. 43-bits from 0x000'00000000 through 0x7FF'FFFFFFFF - see https://docs.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/virtual-address-spaces). However you obviously have to share the space with the actual process hosting the Wasm engine. As Wasm expects a "linear" address space, a range is typically reserved up front, but not committed, then gradually committed as the memory grows. Being that you already have binaries, stack-space, heap, etc... mapped somewhere into that 8TB range, you're not going to be able to reserve anywhere near that amount generally - and that's ignoring the fact you might have multiple memories (or VM contexts/isolates in a browser) in the same process. So being that any "growable" memory needs to reserve a range up front to be able to grow into, each engine is going to need to pick a practical limit well below 8TB if the standard doesn't specify one. (Unless of course they choose to NOT reserve a maximum amount up front, but attempt to reallocate a new range and copy all memory if they outgrow a range, but that would be very slow, and you still have the 8TB cap on total possible user-mode memory to deal with). EDIT: Minor correction: Since Windows 8.1 there is ~128TB of user-mode address space available on x64 (i.e. 47 bits). But the meta-point of how much "linear space" to allow/reserve per Wasm memory on Wasm64 still stands. |
Fortunately, this proposal doesn't change the current wasm behavior of I'm curious if anyone has thoughts about this point:
In particular, we can currently calculate the effective address ( |
@aardappel, I see the complexity for current toolchains. And it's conservative not to allow 32-bit indices for 64-bit memories initially. Perhaps something to investigate empirically further in the future, if other types of toolchains and wasm binaries appear. |
wasm64 is an early prototyping phase, but the name of the target is sufficiently stable that we can add it here. See also WebAssembly/design#1325.
A module importing a 32-bit memory may want to assume the memory has guard pages and thus the module can elide bounds checks. However, 64-bit memories generally cannot use the guard page technique and so they may not want to have any guard pages at all. You could imagine giving 64-bit memories guard pages when the memories were smaller than the guard page size, but this seems an unfortunate impl requirement. So +1 for not having 64-bit memories satisfy 32-bit memory imports. |
I created a new repo for this: https://github.com/WebAssembly/memory64. Let's continue the discussion there. |
wasm64 is an early prototyping phase, but the name of the target is sufficiently stable that we can add it here. See also WebAssembly/design#1325.
Currently wasm only provides up to 4GiB of linear memory. The JS API currently restricts this further to 2 GiB. As mentioned in that issue, some applications are already hitting the 2GiB limit, and will almost certainly hit the 4GiB limit soon after.
As an alternative to wasm64 (which is still very much vaporware), we can introduce 64-bit memories. (See previous discussion) We can extend the current format as follows:
i32
ori64
. (This would also allow us to extend to tables w/ 64-bit indexes later.) If it isi64
, then themin
andmax
fields have the typeu64
.u64
offset inmemarg
. Perhaps this is not necessary though.2**48
(since it is measured in2**16
-byte pages), and would require matching index types.i32
ori64
).memory.init
instructions will usei64
for 64-bit memories.u64
offsets inmemarg
, we can extend the binary format to beu64
, then validate that they are in bounds later.i64
before the min/max values of the limits.In the JS spec, we'll have to decide how to expose this new memory to JS. Is there a maximum ArrayBuffer size (or perhaps maximum array index)? If so, we may need to suggest allowing bigints as an index.
I think that covers everything...? It seems like it should be a pretty minor spec change. It also means we don't need to add any new instructions or sections. What does everyone think?
The text was updated successfully, but these errors were encountered: