Go : философия ("фишки Go")

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go : философия ("фишки Go")

Непрочитанное сообщение Olej » 07 мар 2024, 20:27

Многие гоферы знают, что предварительное выделение памяти для срезов влияет на производительность.
Проверяем это ... и то как происходит переразмещение срезов:

Код: Выделить всё

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
Вложения
slice.go
(261 байт) 6 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go : философия ("фишки Go")

Непрочитанное сообщение Olej » 07 мар 2024, 20:29

Olej писал(а):
07 мар 2024, 20:27
Выглядит так:
Алгоритм изменения размера достаточно хитрый, и он изменялся уже несколько раз, от версии к версии GoLang (и, значит, может меняться и впредь, в поисках оптимальности):
• Для малых размерах среза (до 512 байт) каждое следующее переразмещение удваивается…
• С увеличением запрашиваемого размера коэффициент «запаса» (отношение следующей локации к предыдущей) постепенно уменьшается…
• И для размеров среза порядка 250-300Kb этот коэффициент стабилизируется, и каждое следующее размещение больше предыдущего на 25%
Понятно, что когда для среза длины len() недостаточно ёмкости базового массива cap(), то базовый массив нового размера переразмещается в новую локацию в памяти, и это требует некоторых вычислительных затрат.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go : философия ("фишки Go")

Непрочитанное сообщение Olej » 07 мар 2024, 20:34

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
Хотя это интуитивно и понятно ... но здесь мы это видим в цифрах.
При большом числе активных переразмещений слайсов в пределах фиксированного размера базового массива может в несколько раз сэкономить временные затраты вашей программы.
Вложения
slice2.go
(455 байт) 6 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go : философия ("фишки Go")

Непрочитанное сообщение Olej » 13 мар 2024, 00:01

Рефлексия в Go ... сложный вопрос нужно ли это, и насколько :?: ... но это есть :!:
Пакет reflect
Reflection
Законы рефлексии в Gо
25 июн 2018 в 15:13
Рефлексия — способность программы исследовать собственную структуру, в особенности через типы. Это форма метапрограммирования и отличный источник путаницы.
В Go рефлексия широко используется, например, в пакетах test и fmt. В этой статье попытаемся избавиться от «магии», объяснив, как рефлексия работает в Go.
Рефлексия в Go
2019-01-20
Рефлексия это механизм, с помощью которого программа может проверять своё состояние, исследовать типы данных и менять свою структуру и поведение во время выполнения. Звучит довольно запутано, но давайте раскладывать по полочкам. Во первых рефлексия как механизм есть не во всех языках программирования. В целом она нужна для оперирования свойствами объектов неизвестных в момент компиляции.
Package reflect

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go : философия ("фишки Go")

Непрочитанное сообщение Olej » 13 мар 2024, 00:04

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)
Вложения
refl1.go
(375 байт) 5 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go : философия ("фишки Go")

Непрочитанное сообщение Olej » 13 мар 2024, 00:07

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
Вложения
refl3.go
(675 байт) 5 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go : философия ("фишки Go")

Непрочитанное сообщение Olej » 13 мар 2024, 00:09

Olej писал(а):
13 мар 2024, 00:04
Немного примеров:
То как мы можем разгребать структуру структуры :lol: :

Код: Выделить всё

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
Вложения
refl2.go
(717 байт) 5 скачиваний

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 12 гостей