soa

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2022 License: MIT Imports: 2 Imported by: 0

README

SOA - Structure of Arrays

Go Report Card

Allocate, Grow, Pack or Unpack Golang slices of basic types to optimize memory access for Data-Driven design. The primary motivation is easier manipulation with packed SIMD instructions like SSE2.

Table of Contents

Freatures

  • Allocate a new slice of a given basic type which uses one page of memory.
  • Grow a slice of a given basic type to the next multiple of a page size.
  • Pack all non-zero (0) values of a slice without changing the order.
  • Unpack the values of a slice to the next multiple of a page.

Installation

go get -u github.com/andygeiss/soa

Usage

Allocate

The following function creates new entities with a position (p) and velocity (v), which fits into one page size (ex. 4096 bytes on linux).

func createEntities() (p, v []int32) {
    p = soa.DefaultManager.Allocate([]int32{}).([]int32)
    v = soa.DefaultManager.Allocate([]int32{}).([]int32)
    return
}
Grow

Next we want to ensure that we have enough space to add a new entity to the world, by using the following code.

func ensureSpace(offset, p, v) (pn []int32, vn []int32) {
    // no more space left for a new entity? then grow ... 
    if offset + 1 >= len(p) {
       pn = soa.DefaultManager.Grow(p)
       vn = soa.DefaultManager.Grow(v)
    }
    return p, v
}
Pack

For serialization we dont need to save unused / initialized zero values. Thus, we pack the slices.

type World struct {
    p []int32
    v []int32
}

func saveWorld(w *World, filename string) (err error) {
    // open a new file
    file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
    if err != nil {
	return err
    }
    defer file.Close()
    // create packed world data
    var tmp World
    tmp.p = soa.DefaultManager.Pack(w.p).([]int32)
    tmp.v = soa.DefaultManager.Pack(w.v).([]int32)
    // encode data to JSON
    if err := json.NewEncoder(file).Encode(&tmp); err != nil {
	return err
    }
    return nil
}
Unpack

Next time we load the world from the file and unpack the data:

func loadWorld(filename string) (w *World, err error) {
    // open a new file
    file, err := os.Open(filename)
    if err != nil {
	return nil, err
    }
    defer file.Close()
    // decode data from JSON
    var tmp World
    if err := json.NewDecoder(file).Decode(&tmp); err != nil {
	return nil, err
    }
    tmp.p = soa.DefaultManager.Unpack(tmp.p).([]int32)
    tmp.v = soa.DefaultManager.Unpack(tmp.v).([]int32)
    return &tmp, nil
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultManager = NewManager()

DefaultManager creates a single instance of Manager at startup.

Functions

This section is empty.

Types

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager encapsulates the state and logic.

func NewManager

func NewManager() *Manager

NewManager creates a new Manager and returns its pointer.

func (*Manager) Allocate

func (m *Manager) Allocate(s interface{}) (t interface{})

Allocate creates a new slice s by using a total size of the page size. The length and capacity is set to "page size / size of type". All values are initialized with zero (0).

func (*Manager) Grow

func (m *Manager) Grow(s interface{}) (t interface{})

Grow appends a new page of values to a given slice s and save it as a new slice t.

func (*Manager) Pack

func (m *Manager) Pack(s interface{}) (t interface{})

Pack shrinks the slice s to a total amount of values which are not zero (0). The order of the elements remains the same.

func (*Manager) Unpack

func (m *Manager) Unpack(s interface{}) (t interface{})

Unpack grows the slice s to the next multiple of page size.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL