Login Register
×

WebAssembly Linear Memory

What is Linear Memory in WebAssembly?

Linear memory in WebAssembly is a contiguous block of memory (an array of bytes) that is used by WebAssembly modules to store data. This memory is:

  • Dynamic: It can grow in size during runtime as required.
  • Sandboxed: It is isolated to prevent unauthorized access by other applications or modules.
  • Shared: It can be accessed by both the WebAssembly module and the host environment (e.g., JavaScript).

Characteristics of WebAssembly Linear Memory

  1. Contiguous Block:
    • Memory is represented as a flat, sequential array of bytes, starting at address 0.
  2. Page-Based Allocation:
    • Memory is allocated in units called pages, each 64 KB in size.
  3. Dynamic Growth:
    • Modules can request additional memory pages during runtime, up to a defined maximum.
  4. Sandboxing:
    • Memory is confined to the WebAssembly module, ensuring security and preventing external tampering.

Defining Linear Memory in WebAssembly

Linear memory must be explicitly declared in a WebAssembly module. It can either be defined internally or imported from the host environment.

Example: Declaring Memory in WAT (WebAssembly Text Format)

(module
(memory (export "mem") 1 4) ;; Define memory with an initial size of 1 page and a maximum of 4 pages.
)
  • 1: Initial memory size (1 page = 64 KB).
  • 4: Maximum memory size (4 pages = 256 KB).
  • export “mem”: Exports the memory for host access.

Using Linear Memory in WebAssembly

Linear memory is used to store and retrieve data during the execution of a module. This is done using load and store instructions, which allow you to interact with memory at specific offsets.

Example: Working with Memory in WAT

(module
(memory (export "mem") 1)
;; Store a 32-bit integer at memory offset 0
(func (export "writeMemory")
i32.const 0 ;; Specify the memory offset
i32.const 42 ;; Value to store
i32.store ;; Store the value
)

;; Read a 32-bit integer from memory offset 0
(func (export "readMemory") (result i32)
i32.const 0 ;; Specify the memory offset
i32.load ;; Load the value
)
)

Accessing Linear Memory in JavaScript

The host environment, such as JavaScript, can interact with WebAssemblyโ€™s linear memory via the ArrayBuffer interface. Developers can use typed arrays like Uint8Array or Int32Array to manipulate memory directly.

Example: Reading and Writing Linear Memory in JavaScript

// Instantiate the WebAssembly module
fetch('linear_memory_example.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes))
.then(({ instance }) => {
const memory = new Uint8Array(instance.exports.mem.buffer);

// Write to memory
memory[0] = 65; // ASCII value of 'A'
memory[1] = 66; // ASCII value of 'B'

// Read from memory
console.log(String.fromCharCode(memory[0], memory[1])); // Output: AB
});

Growing Linear Memory

WebAssembly allows memory to grow dynamically by adding pages. This is achieved using the grow method, either in WebAssembly itself or via the host environment.

Example: Growing Memory in JavaScript

const memory = new WebAssembly.Memory({ initial: 1, maximum: 10 }); // Initial size: 1 page, Max size: 10 pages

console.log(memory.buffer.byteLength); // Output: 65536 (64 KB)

memory.grow(2); // Grow memory by 2 pages
console.log(memory.buffer.byteLength); // Output: 196608 (192 KB)

Example: Growing Memory in WAT

(module
(memory (export "mem") 1 10) ;; Memory with max size of 10 pages
(func (export "growMemory") (param i32) (result i32)
memory.grow (local.get 0) ;; Grow memory by the specified number of pages
)
)

Use Cases for Linear Memory

  1. Data Storage:
    • Linear memory is used to store variables, arrays, strings and other data structures.
  2. Interfacing with Host Environment:
    • WebAssembly modules use memory to exchange data with JavaScript or other host environments.
  3. Game Development:
    • Linear memory is ideal for managing large datasets like game states, textures or assets.
  4. Efficient Computation:
    • Store intermediate results for complex computations such as image processing or machine learning.

Best Practices for Using Linear Memory

  1. Pre-Allocate Memory Efficiently:
    • Start with a reasonable initial size to avoid frequent memory growth during runtime.
  2. Avoid Over-Growing:
    • Set a practical maximum size to prevent excessive resource consumption.
  3. Use Typed Arrays:
    • Leverage JavaScript typed arrays for efficient memory access and manipulation.
  4. Keep Offsets Aligned:
    • Align memory offsets (e.g., 4-byte boundaries) to optimize performance for load/store operations.