Исходники и презентации
This commit is contained in:
14
lessons/allocator/allocation_1/main.go
Normal file
14
lessons/allocator/allocation_1/main.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
// -l = disable inlining
|
||||
// -m = print optimization decisions
|
||||
// go build -gcflags '-l -m'
|
||||
|
||||
func getResult() int {
|
||||
result := 200
|
||||
return result
|
||||
}
|
||||
|
||||
func main() {
|
||||
_ = getResult()
|
||||
}
|
||||
14
lessons/allocator/allocation_2/main.go
Normal file
14
lessons/allocator/allocation_2/main.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
// -l = disable inlining
|
||||
// -m = print optimization decisions
|
||||
// go build -gcflags '-l -m'
|
||||
|
||||
func getResult() *int {
|
||||
result := 200
|
||||
return &result
|
||||
}
|
||||
|
||||
func main() {
|
||||
_ = getResult()
|
||||
}
|
||||
15
lessons/allocator/allocation_3/main.go
Normal file
15
lessons/allocator/allocation_3/main.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
// -l = disable inlining
|
||||
// -m = print optimization decisions
|
||||
// go build -gcflags '-l -m'
|
||||
|
||||
func getResult(number *int) int {
|
||||
result := *number + 200
|
||||
return result
|
||||
}
|
||||
|
||||
func main() {
|
||||
number := 100
|
||||
_ = getResult(&number)
|
||||
}
|
||||
28
lessons/allocator/allocation_4/main.go
Normal file
28
lessons/allocator/allocation_4/main.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// -l = disable inlining
|
||||
// -m = print optimization decisions
|
||||
// go build -gcflags '-l -m'
|
||||
|
||||
func printValue(v interface{}) {
|
||||
fmt.Println(v)
|
||||
//_, _ = v.(int)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var num1 int = 10
|
||||
var str1 string = "Hello"
|
||||
|
||||
printValue(num1)
|
||||
printValue(str1)
|
||||
|
||||
var num2 int = 10
|
||||
var str2 string = "Hello"
|
||||
|
||||
var i interface{}
|
||||
i = num2
|
||||
i = str2
|
||||
_ = i
|
||||
}
|
||||
18
lessons/allocator/allocation_5/main.go
Normal file
18
lessons/allocator/allocation_5/main.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
// -l = disable inlining
|
||||
// -m = print optimization decisions
|
||||
// go build -gcflags '-l -m'
|
||||
|
||||
func createPointer() *int {
|
||||
value2 := new(int)
|
||||
return value2
|
||||
}
|
||||
|
||||
func main() {
|
||||
value1 := new(int) // stack
|
||||
_ = value1
|
||||
|
||||
value2 := createPointer() // heap
|
||||
_ = value2
|
||||
}
|
||||
33
lessons/allocator/allocation_effect_1/allocation_test.go
Normal file
33
lessons/allocator/allocation_effect_1/allocation_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
// go test -bench=. -benchmem
|
||||
|
||||
type Data struct {
|
||||
iValue int
|
||||
sValue string
|
||||
bValue bool
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func NewDataByValue() Data {
|
||||
return Data{iValue: 100, sValue: "100", bValue: true}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func NewDataByPointer() *Data {
|
||||
return &Data{iValue: 100, sValue: "100", bValue: true}
|
||||
}
|
||||
|
||||
func BenchmarkNewByValue(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = NewDataByValue()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewByPointer(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = NewDataByPointer()
|
||||
}
|
||||
}
|
||||
43
lessons/allocator/allocation_effect_2/allocation_test.go
Normal file
43
lessons/allocator/allocation_effect_2/allocation_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
// go test -bench=. -benchmem
|
||||
|
||||
const bufferSize = 10
|
||||
|
||||
type ReaderWithSliceArgument struct{}
|
||||
|
||||
func (r ReaderWithSliceArgument) Read(p []byte) (int, error) {
|
||||
for i := 0; i < bufferSize; i++ {
|
||||
p[i] = byte(i)
|
||||
}
|
||||
|
||||
return bufferSize, nil
|
||||
}
|
||||
|
||||
type ReaderWithSliceReturn struct{}
|
||||
|
||||
func (r ReaderWithSliceReturn) Read(n int) ([]byte, error) {
|
||||
p := make([]byte, n)
|
||||
for i := 0; i < n; i++ {
|
||||
p[i] = byte(i)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func BenchmarkSliceWithArgument(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := make([]byte, bufferSize)
|
||||
reader := ReaderWithSliceArgument{}
|
||||
_, _ = reader.Read(p)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSliceWithReturn(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
reader := ReaderWithSliceReturn{}
|
||||
_, _ = reader.Read(bufferSize)
|
||||
}
|
||||
}
|
||||
28
lessons/allocator/allocation_effect_3/allocation_test.go
Normal file
28
lessons/allocator/allocation_effect_3/allocation_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
// go test -bench=. -benchmem
|
||||
|
||||
type Data struct {
|
||||
pointer *int
|
||||
}
|
||||
|
||||
func BenchmarkIteration1(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var number int
|
||||
data := &Data{
|
||||
pointer: &number,
|
||||
}
|
||||
_ = data
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIteration2(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var number int
|
||||
data := &Data{}
|
||||
data.pointer = &number
|
||||
_ = data
|
||||
}
|
||||
}
|
||||
26
lessons/allocator/allocations_offset/main.go
Normal file
26
lessons/allocator/allocations_offset/main.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// For memory blocks larger than 32768 bytes,
|
||||
// each of them is always composed of multiple
|
||||
// memory pages. The memory page size used by the
|
||||
// official standard Go runtime (1.22 versions) is 8192 bytes.
|
||||
|
||||
func main() {
|
||||
var data1 [32769]byte
|
||||
var data2 [32769]byte
|
||||
|
||||
data1Pointer := unsafe.Pointer(&data1)
|
||||
data2Pointer := unsafe.Pointer(&data2)
|
||||
|
||||
fmt.Println("adress:", data1Pointer, data2Pointer)
|
||||
fmt.Println("size:", unsafe.Sizeof(data1), unsafe.Sizeof(data2))
|
||||
|
||||
distance := uintptr(data2Pointer) - uintptr(data1Pointer)
|
||||
fmt.Println("distance:", distance)
|
||||
fmt.Println("waste:", distance-unsafe.Sizeof(data1))
|
||||
}
|
||||
24
lessons/allocator/allocations_size/main.go
Normal file
24
lessons/allocator/allocations_size/main.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var result string
|
||||
var buffer []byte = make([]byte, 33)
|
||||
|
||||
func Concat(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
result = string(buffer) + string(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
b := testing.Benchmark(Concat)
|
||||
fmt.Println(b.AllocsPerOp()) // 3
|
||||
fmt.Println(b.AllocedBytesPerOp()) // 176
|
||||
|
||||
// alocated 176 bytes = 48 (not 33) + 48 (not 33) + 80 (not 33 + 33 = 66)
|
||||
// waste = 15 + 15 + 14 = 44 bytes (25% waste)
|
||||
}
|
||||
30
lessons/allocator/arena_api/main.go
Normal file
30
lessons/allocator/arena_api/main.go
Normal file
@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
// GOEXPERIMENT=arenas go run main.go
|
||||
// go run -tags goexperiment.arenas main.go
|
||||
|
||||
import (
|
||||
"arena"
|
||||
)
|
||||
|
||||
type Data struct {
|
||||
deposit int
|
||||
credit int
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := arena.NewArena()
|
||||
defer a.Free()
|
||||
|
||||
value := arena.New[int64](a)
|
||||
_ = value
|
||||
|
||||
data := arena.New[Data](a)
|
||||
_ = data
|
||||
|
||||
slice := arena.MakeSlice[int32](a, 0, 10)
|
||||
_ = slice
|
||||
|
||||
cloned := arena.Clone[*Data](data) // moved to heap
|
||||
_ = cloned
|
||||
}
|
||||
24
lessons/allocator/arena_problem_1/main.go
Normal file
24
lessons/allocator/arena_problem_1/main.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
// GOEXPERIMENT=arenas go run main.go
|
||||
// go run -tags goexperiment.arenas main.go
|
||||
|
||||
import (
|
||||
"arena"
|
||||
)
|
||||
|
||||
type Data struct {
|
||||
value int
|
||||
operaions []int
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := arena.NewArena()
|
||||
defer a.Free()
|
||||
|
||||
// Arenas will not allocate all reference types automatically
|
||||
operations := arena.MakeSlice[int](a, 0, 100)
|
||||
data := arena.New[Data](a)
|
||||
data.operaions = operations
|
||||
_ = data
|
||||
}
|
||||
18
lessons/allocator/arena_problem_2/main.go
Normal file
18
lessons/allocator/arena_problem_2/main.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
// GOEXPERIMENT=arenas go run main.go
|
||||
// go run -tags goexperiment.arenas main.go
|
||||
|
||||
import (
|
||||
"arena"
|
||||
)
|
||||
|
||||
func main() {
|
||||
mem := arena.NewArena()
|
||||
defer mem.Free()
|
||||
|
||||
slice := arena.NewSlice[int](mem, 0, 5)
|
||||
slice = append(slice, 1, 2, 3, 4, 5)
|
||||
|
||||
slice = append(slice, 6) // moved to heap
|
||||
}
|
||||
23
lessons/allocator/arena_problem_3/main.go
Normal file
23
lessons/allocator/arena_problem_3/main.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
// GOEXPERIMENT=arenas go run main.go
|
||||
// go run -tags goexperiment.arenas main.go
|
||||
|
||||
import (
|
||||
"arena"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Data struct {
|
||||
deposit int
|
||||
credit int
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := arena.NewArena()
|
||||
data := arena.New[Data](a)
|
||||
a.Free()
|
||||
|
||||
// use after free
|
||||
fmt.Println(data)
|
||||
}
|
||||
22
lessons/allocator/big_allocations/main.go
Normal file
22
lessons/allocator/big_allocations/main.go
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// go run main.go
|
||||
// GOGC=off go run main.go
|
||||
|
||||
var data []byte
|
||||
|
||||
func main() {
|
||||
count := 0
|
||||
|
||||
for {
|
||||
data = make([]byte, 1<<30)
|
||||
/*for idx := 0; idx < 1<<30; idx += 4096 {
|
||||
data[idx] = 100
|
||||
}*/
|
||||
|
||||
fmt.Println("allocated GB:", count)
|
||||
count++
|
||||
}
|
||||
}
|
||||
74
lessons/allocator/linear_allocator/main.go
Normal file
74
lessons/allocator/linear_allocator/main.go
Normal file
@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type LinearAllocator struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func NewLinearAllocator(capacity int) (LinearAllocator, error) {
|
||||
if capacity <= 0 {
|
||||
return LinearAllocator{}, errors.New("incorrect capacity")
|
||||
}
|
||||
|
||||
return LinearAllocator{
|
||||
data: make([]byte, 0, capacity),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *LinearAllocator) Allocate(size int) (unsafe.Pointer, error) {
|
||||
previousLength := len(a.data)
|
||||
newLength := previousLength + size
|
||||
|
||||
if newLength > cap(a.data) {
|
||||
// can increase capacity
|
||||
return nil, errors.New("not enough memory")
|
||||
}
|
||||
|
||||
a.data = a.data[:newLength]
|
||||
pointer := unsafe.Pointer(&a.data[previousLength])
|
||||
return pointer, nil
|
||||
}
|
||||
|
||||
// not supported by this kind of allocator
|
||||
// func (a *LinearAllocator) Deallocate(pointer unsafe.Pointer) error {}
|
||||
|
||||
func (a *LinearAllocator) Free() {
|
||||
a.data = a.data[:0]
|
||||
}
|
||||
|
||||
func store[T any](pointer unsafe.Pointer, value T) {
|
||||
*(*T)(pointer) = value
|
||||
}
|
||||
|
||||
func load[T any](pointer unsafe.Pointer) T {
|
||||
return *(*T)(pointer)
|
||||
}
|
||||
|
||||
func main() {
|
||||
const MB = 1 << 20
|
||||
allocator, err := NewLinearAllocator(MB)
|
||||
if err != nil {
|
||||
// handling...
|
||||
}
|
||||
|
||||
defer allocator.Free()
|
||||
|
||||
pointer1, _ := allocator.Allocate(2)
|
||||
pointer2, _ := allocator.Allocate(4)
|
||||
|
||||
store[int16](pointer1, 100)
|
||||
store[int32](pointer2, 200)
|
||||
|
||||
value1 := load[int16](pointer1)
|
||||
value2 := load[int32](pointer2)
|
||||
fmt.Println("value1:", value1)
|
||||
fmt.Println("value2:", value2)
|
||||
|
||||
fmt.Println("address1:", pointer1)
|
||||
fmt.Println("address2:", pointer2)
|
||||
}
|
||||
26
lessons/allocator/loop_allocations/allocations_test.go
Normal file
26
lessons/allocator/loop_allocations/allocations_test.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
// go test -bench=. allocations_test.go -benchmem
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
//go:noinline
|
||||
func Initialize(value *int) {
|
||||
*value = 1000
|
||||
}
|
||||
|
||||
func BenchmarkWithoutLoopAllocation(b *testing.B) {
|
||||
var value int
|
||||
for i := 0; i < b.N; i++ {
|
||||
Initialize(&value)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWithLoopAllocation(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var value int
|
||||
Initialize(&value)
|
||||
}
|
||||
}
|
||||
50
lessons/allocator/pool/pool_test.go
Normal file
50
lessons/allocator/pool/pool_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
// go test -bench=. pool_test.go -benchmem
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Person struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type PersonsPool struct {
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
func NewPersonsPool() *PersonsPool {
|
||||
return &PersonsPool{
|
||||
pool: sync.Pool{
|
||||
New: func() interface{} { return new(Person) },
|
||||
},
|
||||
}
|
||||
}
|
||||
func (p *PersonsPool) Get() *Person {
|
||||
return p.pool.Get().(*Person)
|
||||
}
|
||||
|
||||
func (p *PersonsPool) Put(person *Person) {
|
||||
p.pool.Put(person)
|
||||
}
|
||||
|
||||
var gPerson *Person
|
||||
|
||||
func BenchmarkWithPool(b *testing.B) {
|
||||
pool := NewPersonsPool()
|
||||
for i := 0; i < b.N; i++ {
|
||||
person := pool.Get()
|
||||
person.name = "Ivan" // need to reset values
|
||||
gPerson = person
|
||||
pool.Put(person)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWithoutPool(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
person := &Person{name: "Ivan"}
|
||||
gPerson = person
|
||||
}
|
||||
}
|
||||
100
lessons/allocator/pool_allocator/main.go
Normal file
100
lessons/allocator/pool_allocator/main.go
Normal file
@ -0,0 +1,100 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PoolAllocator struct {
|
||||
objectPool []byte
|
||||
freeObjects map[unsafe.Pointer]struct{}
|
||||
objectSize int
|
||||
}
|
||||
|
||||
func NewPoolAllocator(capacity int, objectSize int) (PoolAllocator, error) {
|
||||
if capacity <= 0 || objectSize <= 0 || capacity%objectSize != 0 {
|
||||
return PoolAllocator{}, errors.New("incorrect argumnets")
|
||||
}
|
||||
|
||||
allocator := PoolAllocator{
|
||||
objectPool: make([]byte, capacity),
|
||||
freeObjects: make(map[unsafe.Pointer]struct{}, capacity/objectSize),
|
||||
objectSize: objectSize,
|
||||
}
|
||||
|
||||
allocator.resetMemoryState()
|
||||
return allocator, nil
|
||||
}
|
||||
|
||||
func (a *PoolAllocator) Allocate() (unsafe.Pointer, error) {
|
||||
if len(a.freeObjects) == 0 {
|
||||
// can increase capacity
|
||||
return nil, errors.New("not enough memory")
|
||||
}
|
||||
|
||||
var pointer unsafe.Pointer
|
||||
for freePointer := range a.freeObjects {
|
||||
pointer = freePointer
|
||||
break
|
||||
}
|
||||
|
||||
delete(a.freeObjects, pointer)
|
||||
return pointer, nil
|
||||
}
|
||||
|
||||
func (a *PoolAllocator) Deallocate(pointer unsafe.Pointer) error {
|
||||
if pointer == nil {
|
||||
return errors.New("incorrect pointer")
|
||||
}
|
||||
|
||||
// potentionally incorrect pointer
|
||||
a.freeObjects[pointer] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PoolAllocator) Free() {
|
||||
a.resetMemoryState()
|
||||
}
|
||||
|
||||
func (a *PoolAllocator) resetMemoryState() {
|
||||
for offset := 0; offset < len(a.objectPool); offset += a.objectSize {
|
||||
pointer := unsafe.Pointer(&a.objectPool[offset])
|
||||
a.freeObjects[pointer] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func store[T any](pointer unsafe.Pointer, value T) {
|
||||
*(*T)(pointer) = value
|
||||
}
|
||||
|
||||
func load[T any](pointer unsafe.Pointer) T {
|
||||
return *(*T)(pointer)
|
||||
}
|
||||
|
||||
func main() {
|
||||
const KB = 1 << 10
|
||||
allocator, err := NewPoolAllocator(KB, 4)
|
||||
if err != nil {
|
||||
// handling...
|
||||
}
|
||||
|
||||
defer allocator.Free()
|
||||
|
||||
pointer1, _ := allocator.Allocate()
|
||||
pointer2, _ := allocator.Allocate()
|
||||
|
||||
store[int32](pointer1, 100)
|
||||
store[int32](pointer2, 200)
|
||||
|
||||
value1 := load[int32](pointer1)
|
||||
value2 := load[int32](pointer2)
|
||||
fmt.Println("value1:", value1)
|
||||
fmt.Println("value2:", value2)
|
||||
|
||||
fmt.Println("address1:", pointer1)
|
||||
fmt.Println("address2:", pointer2)
|
||||
|
||||
allocator.Deallocate(pointer1)
|
||||
allocator.Deallocate(pointer2)
|
||||
}
|
||||
100
lessons/allocator/stack_allocator/main.go
Normal file
100
lessons/allocator/stack_allocator/main.go
Normal file
@ -0,0 +1,100 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const headerSize = 2
|
||||
|
||||
type StackAllocator struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func NewStackAllocator(capacity int) (StackAllocator, error) {
|
||||
if capacity <= 0 {
|
||||
return StackAllocator{}, errors.New("incorrect capacity")
|
||||
}
|
||||
|
||||
return StackAllocator{
|
||||
data: make([]byte, 0, capacity),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *StackAllocator) Allocate(size int) (unsafe.Pointer, error) {
|
||||
if size > math.MaxInt16 {
|
||||
// can increase header size
|
||||
return nil, errors.New("incorrect size")
|
||||
}
|
||||
|
||||
previousLength := len(a.data)
|
||||
newLength := previousLength + headerSize + size
|
||||
|
||||
if newLength > cap(a.data) {
|
||||
// can increase capacity
|
||||
return nil, errors.New("not enough memory")
|
||||
}
|
||||
|
||||
a.data = a.data[:newLength]
|
||||
header := unsafe.Pointer(&a.data[previousLength])
|
||||
pointer := unsafe.Pointer(&a.data[previousLength+headerSize])
|
||||
|
||||
*(*int16)(header) = int16(size)
|
||||
return pointer, nil
|
||||
}
|
||||
|
||||
func (a *StackAllocator) Deallocate(pointer unsafe.Pointer) error {
|
||||
// can deallocate without pointer
|
||||
if pointer == nil {
|
||||
return errors.New("incorrect pointer")
|
||||
}
|
||||
|
||||
header := unsafe.Add(pointer, -headerSize)
|
||||
size := *(*int16)(header)
|
||||
|
||||
previousLength := len(a.data)
|
||||
newLength := previousLength - headerSize - int(size)
|
||||
|
||||
a.data = a.data[:newLength]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *StackAllocator) Free() {
|
||||
a.data = a.data[:0]
|
||||
}
|
||||
|
||||
func store[T any](pointer unsafe.Pointer, value T) {
|
||||
*(*T)(pointer) = value
|
||||
}
|
||||
|
||||
func load[T any](pointer unsafe.Pointer) T {
|
||||
return *(*T)(pointer)
|
||||
}
|
||||
|
||||
func main() {
|
||||
const KB = 1 << 10
|
||||
allocator, err := NewStackAllocator(KB)
|
||||
if err != nil {
|
||||
// handling...
|
||||
}
|
||||
|
||||
defer allocator.Free()
|
||||
|
||||
pointer1, _ := allocator.Allocate(2)
|
||||
defer allocator.Deallocate(pointer1)
|
||||
pointer2, _ := allocator.Allocate(4)
|
||||
defer allocator.Deallocate(pointer2)
|
||||
|
||||
store[int16](pointer1, 100)
|
||||
store[int32](pointer2, 200)
|
||||
|
||||
value1 := load[int16](pointer1)
|
||||
value2 := load[int32](pointer2)
|
||||
fmt.Println("value1:", value1)
|
||||
fmt.Println("value2:", value2)
|
||||
|
||||
fmt.Println("address1:", pointer1)
|
||||
fmt.Println("address2:", pointer2)
|
||||
}
|
||||
20
lessons/allocator/stack_growth/main.go
Normal file
20
lessons/allocator/stack_growth/main.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
// -l = disable inlining
|
||||
// -m = print optimization decisions
|
||||
// go build -gcflags '-l -m'
|
||||
// go run -gcflags '-l' main.go
|
||||
|
||||
func process(index int) byte {
|
||||
var data [1 << 20]byte
|
||||
return data[index]
|
||||
}
|
||||
|
||||
func main() {
|
||||
var index int = 100
|
||||
|
||||
pointer := &index
|
||||
println("pointer:", pointer)
|
||||
process(index)
|
||||
println("pointer:", pointer)
|
||||
}
|
||||
Reference in New Issue
Block a user