Исходники и презентации

This commit is contained in:
2025-05-23 07:26:39 +03:00
parent aa948179d5
commit 02d8430a3a
514 changed files with 13773 additions and 0 deletions

View 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()
}

View 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()
}

View 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)
}

View 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
}

View 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
}

View 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()
}
}

View 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)
}
}

View 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
}
}

View 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))
}

View 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)
}

View 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
}

View 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
}

View 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
}

View 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)
}

View 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++
}
}

View 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)
}

View 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)
}
}

View 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
}
}

View 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)
}

View 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)
}

View 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)
}