Страница 3 из 3
Go : философия ("фишки Go")
Добавлено: 07 мар 2024, 20:27
Olej
Многие гоферы знают, что предварительное выделение памяти для срезов влияет на производительность.
Проверяем это ... и то как происходит переразмещение срезов:
Код: Выделить всё
package main
import "fmt"
func main() {
cap1 := 0
arr := make([]byte, 0, cap1)
for cap(arr) < 20000000 {
arr = append(arr, 1)
if cap(arr) != cap1 {
fmt.Printf("%10d | %f\n",
cap(arr), float64(cap(arr))/float64(cap1))
cap1 = cap(arr)
}
}
}
Выглядит так:
Код: Выделить всё
olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types$ go run slice.go
8 | +Inf
16 | 2.000000
32 | 2.000000
64 | 2.000000
128 | 2.000000
256 | 2.000000
512 | 2.000000
896 | 1.750000
1408 | 1.571429
2048 | 1.454545
3072 | 1.500000
4096 | 1.333333
5376 | 1.312500
6912 | 1.285714
9472 | 1.370370
12288 | 1.297297
16384 | 1.333333
21760 | 1.328125
28672 | 1.317647
40960 | 1.428571
57344 | 1.400000
73728 | 1.285714
98304 | 1.333333
131072 | 1.333333
172032 | 1.312500
221184 | 1.285714
278528 | 1.259259
352256 | 1.264706
442368 | 1.255814
557056 | 1.259259
704512 | 1.264706
884736 | 1.255814
1114112 | 1.259259
1400832 | 1.257353
1753088 | 1.251462
2195456 | 1.252336
2752512 | 1.253731
3448832 | 1.252976
4317184 | 1.251781
5398528 | 1.250474
6750208 | 1.250379
8445952 | 1.251214
10559488 | 1.250242
13205504 | 1.250582
16515072 | 1.250620
20652032 | 1.250496
Go : философия ("фишки Go")
Добавлено: 07 мар 2024, 20:29
Olej
Olej писал(а): ↑07 мар 2024, 20:27
Выглядит так:
Алгоритм изменения размера достаточно хитрый, и он изменялся уже несколько раз, от версии к версии GoLang (и, значит, может меняться и впредь, в поисках оптимальности):
• Для малых размерах среза (до 512 байт) каждое следующее переразмещение удваивается…
• С увеличением запрашиваемого размера коэффициент «запаса» (отношение следующей локации к предыдущей) постепенно уменьшается…
• И для размеров среза порядка 250-300Kb этот коэффициент стабилизируется, и каждое следующее размещение больше предыдущего на 25%
Понятно, что когда для среза длины len() недостаточно ёмкости базового массива cap(), то базовый массив нового размера переразмещается в новую локацию в памяти, и это требует некоторых вычислительных затрат.
Go : философия ("фишки Go")
Добавлено: 07 мар 2024, 20:34
Olej
Olej писал(а): ↑07 мар 2024, 20:29
и это требует некоторых вычислительных затрат.
И это можно наблюдать в эксперименте:
Код: Выделить всё
package main
import (
"fmt"
"os"
"strconv"
"time"
)
var n int = 20000000
func remap(arr []byte) {
i := 0
t0 := time.Now()
defer func() {
fmt.Printf("время %d размещений %v\n",
i, time.Now().Sub(t0))
}()
for ; len(arr) < n; i++ {
arr = append(arr, 0)
}
}
func main() {
base := *new([500000000]byte)
if len(os.Args) > 1 {
n, _ = strconv.Atoi(os.Args[1])
}
remap(*new([]byte)) // 0:0
remap(base[0:0]) // 0:∞
}
В 1-й раз происходит многократное переразмещение с переносом базового массива по памяти, во 2-й - все переразмещения с пределах одного большого массива.
Наблюдаем:
Код: Выделить всё
olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types$ go run slice2.go
время 20000000 размещений 120.456191ms
время 20000000 размещений 39.242947ms
Код: Выделить всё
olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types$ go run slice2.go 100000
время 100000 размещений 562.588µs
время 100000 размещений 250.031µs
Код: Выделить всё
olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types$ go run slice2.go 200000000
время 200000000 размещений 1.156671745s
время 200000000 размещений 630.835636ms
Хотя это интуитивно и понятно ... но здесь мы это видим в цифрах.
При большом числе активных переразмещений слайсов в пределах фиксированного размера базового массива может в несколько раз сэкономить временные затраты вашей программы.
Go : философия ("фишки Go")
Добавлено: 13 мар 2024, 00:01
Olej
Рефлексия в Go ... сложный вопрос нужно ли это, и насколько
... но это есть
Пакет
reflect
Reflection
Законы рефлексии в Gо
25 июн 2018 в 15:13
Рефлексия — способность программы исследовать собственную структуру, в особенности через типы. Это форма метапрограммирования и отличный источник путаницы.
В Go рефлексия широко используется, например, в пакетах test и fmt. В этой статье попытаемся избавиться от «магии», объяснив, как рефлексия работает в Go.
Рефлексия в Go
2019-01-20
Рефлексия это механизм, с помощью которого программа может проверять своё состояние, исследовать типы данных и менять свою структуру и поведение во время выполнения. Звучит довольно запутано, но давайте раскладывать по полочкам. Во первых рефлексия как механизм есть не во всех языках программирования. В целом она нужна для оперирования свойствами объектов неизвестных в момент компиляции.
Package reflect
Go : философия ("фишки Go")
Добавлено: 13 мар 2024, 00:04
Olej
Olej писал(а): ↑13 мар 2024, 00:01
Рефлексия в Go
Немного примеров:
Код: Выделить всё
package main
import (
"fmt"
"reflect"
)
func main() {
var x complex128 = 3.4 + 4.3i
var (t reflect.Type
v reflect.Value)
t = reflect.TypeOf(x)
v = reflect.ValueOf(x)
fmt.Println(x, t, v)
type mycomplex complex128
y := mycomplex(x)
t = reflect.TypeOf(y)
v = reflect.ValueOf(y)
fmt.Println(y, t, v)
}
Код: Выделить всё
olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/reflect$ go run refl1.go
(3.4+4.3i) complex128 (3.4+4.3i)
(3.4+4.3i) main.mycomplex (3.4+4.3i)
Go : философия ("фишки Go")
Добавлено: 13 мар 2024, 00:07
Olej
Olej писал(а): ↑13 мар 2024, 00:04
Немного примеров:
Код: Выделить всё
package main
import (
"fmt"
"reflect"
)
func main() {
arr := []any{"строка", 42, 42.2, 3.4 + 4.3i, func(){}}
for _, v := range arr {
switch v := reflect.ValueOf(v); v.Kind() {
case reflect.String:
fmt.Print(v.String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fmt.Print(v.Int())
case reflect.Float32, reflect.Float64:
fmt.Print(v.Float())
case reflect.Complex64, reflect.Complex128:
fmt.Print(v.Complex())
default:
fmt.Printf("непонятный тип: %s", v.Kind())
}
println()
}
}
Код: Выделить всё
olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/reflect$ go run refl3.go
строка
42
42.2
(3.4+4.3i)
непонятный тип: func
Go : философия ("фишки Go")
Добавлено: 13 мар 2024, 00:09
Olej
Olej писал(а): ↑13 мар 2024, 00:04
Немного примеров:
То как мы можем разгребать структуру структуры
:
Код: Выделить всё
package main
import (
"fmt"
"reflect"
)
func main() {
type Data struct {
f1 string
f2 int16
f3 float64
f4 []byte
f5 map[int]int
}
var x Data
x = Data{"строка!", 11, 12.0,
[]byte{1, 2, 3, 4, 5},
map[int]int{1:5, 2:4, 3:3}}
t := reflect.TypeOf(x)
fmt.Println("тип:", t.Kind(), "число полей:", t.NumField())
for i := range t.NumField() {
fmt.Print(i + 1, ":",
" имя=", t.Field(i).Name,
" тип=", t.Field(i).Type.Kind(),
)
fv := reflect.ValueOf(t.Field(i))
fmt.Print(" => ",fv.Field(2))
println()
}
}
Код: Выделить всё
olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/reflect$ go run refl2.go
тип: struct число полей: 5
1: имя=f1 тип=string => string
2: имя=f2 тип=int16 => int16
3: имя=f3 тип=float64 => float64
4: имя=f4 тип=slice => []uint8
5: имя=f5 тип=map => map[int]int