Если мы хотим просмотреть массив, коллекцию карт, фрагмент среза и т. д., язык Go (Golang) предоставляет нам более полезный метод For Range. Range – это ключевое слово, представляющее диапазон. Его можно использовать вместе с for для перебора коллекций, таких как массивы и Карты. Его использование лаконично, а карта, канал и т. д. также используются для диапазона, поэтому в кодировании мы используемfor range
Выполнение итераций цикла - это самое то. Для этой наиболее часто используемой итерации, особенно иfor i=0;i<N;i++
Как производительность в сравнении? Разберем следующий пример, давайтеfor range
Более глубокое понимание циклов позволяет нам писать программы с более высокой производительностью.
Основное использование
for range
Использование очень простое, и здесь демонстрируется использование следующих двух типов коллекций.
package main
import "fmt"
func main() {
ages:=[]string{"10", "20", "30"}
for i,age:=range ages{
fmt.Println(i,age)
}
}
Это для итеративного использования слайсов Slice, используяrange
ключевое слово возвращает две переменныеi,age
, первое — это индекс слайса Slice, а второе — содержимое слайса Slice, поэтому мы печатаем:
0 10
1 20
2 30
О нарезке Slice в языке Go вы можете обратиться к этой статье, которую я написал ранее.Боевые заметки на языке Go (5) | Go slice
Давайте посмотрим на карту (словарь) нижеfor range
Используйте примеры.
package main
import "fmt"
func main() {
ages:=map[string]int{"张三":15,"李四":20,"王武":36}
for name,age:=range ages{
fmt.Println(name,age)
}
}
в настоящее время используетfor range
При переборе карты первая возвращаемая переменнаяkey
, вторая переменнаяvalue
, что соответствуетname
иages
. Давайте запустим программу и посмотрим на результат.
张三 15
李四 20
王武 36
Здесь следует отметить, что,for range map
возвращениеK-V
Порядок пар ключ-значение не фиксирован, он случайный, на этот раз он может быть张三-15
Первый появится, следующий запуск может быть王武-36
Печатается первый.
Для получения более подробной информации о Map, пожалуйста, обратитесь к моей предыдущей статьеБоевые заметки о языке Go (6) | Go Map.
Регулярное сравнение циклов
Например, для срезов Slice у нас есть две итерации: одна обычнаяfor i:=0;i<N;i++
путь; одинfor range
Кстати, ниже мы рассмотрим производительность двух итераций.
func ForSlice(s []string) {
len := len(s)
for i := 0; i < len; i++ {
_, _ = i, s[i]
}
}
func RangeForSlice(s []string) {
for i, v := range s {
_, _ = i, v
}
}
Для тестирования я написал эти две функции, которые зацикливают и повторяют срезы Slice.С точки зрения реализации, их логика одинакова, что гарантирует, что мы можем тестировать в той же ситуации.
import "testing"
const N = 1000
func initSlice() []string{
s:=make([]string,N)
for i:=0;i<N;i++{
s[i]="www.flysnow.org"
}
return s;
}
func BenchmarkForSlice(b *testing.B) {
s:=initSlice()
b.ResetTimer()
for i:=0; i<b.N;i++ {
ForSlice(s)
}
}
func BenchmarkRangeForSlice(b *testing.B) {
s:=initSlice()
b.ResetTimer()
for i:=0; i<b.N;i++ {
RangeForSlice(s)
}
}
Варианты использования этого теста Bench находятся в одной и той же ситуации, моделируя обход среза длиной 1000. Затем мы бежимgo test -bench=. -run=NONE
Посмотреть результаты теста производительности.
BenchmarkForSlice-4 5000000 287 ns/op
BenchmarkRangeForSlice-4 3000000 509 ns/op
Как видно из теста производительности, обычный цикл for более эффективен, чемfor range
Производительностьfor range
Каждый раз это копия элементов цикла, поэтому чем сложнее бюджет в коллекции, тем хуже производительность.В отличие от обычного цикла for, он получает элементы в коллекции черезs[i]
, этот метод ссылки на указатель индекса намного выше, чем производительность копирования.
Поскольку это проблема копирования элементов, цель итерации срезов Slice — получение элементов, тогда мы реализуем это другим способом.for range
.
func RangeForSlice(s []string) {
for i, _ := range s {
_, _ = i, s[i]
}
}
Теперь давайте снова запустим тест производительности Benchmark, чтобы увидеть результат.
BenchmarkForSlice-4 5000000 280 ns/op
BenchmarkRangeForSlice-4 5000000 277 ns/op
Ну, как мы и думали, производительность поднялась, и она на уровне обычного цикла for. Причина в том, что мы проходим_
отбрасывает копирование элемента, затем проходитs[i]
Получение элементов итерации повышает производительность и служит цели.
Обход карты
Для карты мы не можем использоватьfor i:=0;i<N;i++
Кстати, если у вас есть всеkey
За исключением списка элементов, поэтому в большинстве случаев мы используемfor range
Путь.
func RangeForMap1(m map[int]string) {
for k, v := range m {
_, _ = k, v
}
}
const N = 1000
func initMap() map[int]string {
m := make(map[int]string, N)
for i := 0; i < N; i++ {
m[i] = fmt.Sprint("www.flysnow.org",i)
}
return m
}
func BenchmarkRangeForMap1(b *testing.B) {
m:=initMap()
b.ResetTimer()
for i := 0; i < b.N; i++ {
RangeForMap1(m)
}
}
Эта статья является оригинальной статьей, перепечатайте ее и укажите источник: «Всегда есть плохие люди, которые удаляют мое первоначальное описание, когда берут статью».
flysnow_org
или сайтwww.flysnow.org/, и впервые прочитал последующие замечательные статьи. «Замечания против гнилых людей **...&*¥» Если вы считаете, что это хорошо, поделитесь им в кругу друзей, спасибо за вашу поддержку.
Приведенный выше пример — это функция обхода карты и бенчмарк-тест. Я написал их вместе. Запустите тест, чтобы увидеть эффект.
BenchmarkForSlice-8 5000000 298 ns/op
BenchmarkRangeForSlice-8 3000000 475 ns/op
BenchmarkRangeForMap1-8 100000 14531 ns/op
По сравнению со Slice производительность обхода карты хуже, что можно назвать ужасающей. Что ж, приступим к оптимизации, и идея в том, чтобы уменьшить ценность копирования. Причина, по которой RangeForSlice в тесте тоже медленный, заключается в том, что я восстановил RangeForSlice до достойной копии, чтобы сравнить производительность.
func RangeForMap2(m map[int]string) {
for k, _ := range m {
_, _ = k, m[k]
}
}
func BenchmarkRangeForMap2(b *testing.B) {
m := initMap()
b.ResetTimer()
for i := 0; i < b.N; i++ {
RangeForMap2(m)
}
}
Запустите тест производительности еще раз, чтобы увидеть эффект.
BenchmarkForSlice-8 5000000 298 ns/op
BenchmarkRangeForSlice-8 3000000 475 ns/op
BenchmarkRangeForMap1-8 100000 14531 ns/op
BenchmarkRangeForMap2-8 100000 23199 ns/op
Эм, вы нашли что-то не так, методBenchmarkRangeForMap2
Производительность значительно упала, что видно по затратам времени на каждую операцию (хотя количество запусков теста производительности в секунду осталось прежним). В отличие от Slice, который мы тестировали выше, на этот раз не только не улучшилось, но и ухудшилось.
продолжить редактированиеMap2
Реализация функции такова:
func RangeForMap2(m map[int]Person) {
for range m {
}
}
Ничего не делайте, просто повторите и снова запустите тест производительности.
BenchmarkForSlice-8 5000000 301 ns/op
BenchmarkRangeForSlice-8 3000000 478 ns/op
BenchmarkRangeForMap1-8 100000 14822 ns/op
BenchmarkRangeForMap2-8 100000 14215 ns/op
*Мы поражены тем, что ничего не делаем, и получаемK-V
Производительность операции значения такая же, и она полностью отличается от Slice, если не сказатьfor range
А как насчет эффективности потери копии? Куда все ушли? Угадайте все, вы можете комбинировать принципы следующего раздела для достижения
по принципу диапазона
просмотревGitHub.com/go wave/go fr…исходный код, мы можем найтиfor range
Реализация:
// Arrange to do a loop appropriate for the type. We will produce
// for INIT ; COND ; POST {
// ITER_INIT
// INDEX = INDEX_TEMP
// VALUE = VALUE_TEMP // If there is a value
// original statements
// }
И есть разные реализации компиляции для Slice, Map и т. д., давайте сначала посмотримfor range slice
конкретная реализация
// The loop we generate:
// for_temp := range
// len_temp := len(for_temp)
// for index_temp = 0; index_temp < len_temp; index_temp++ {
// value_temp = for_temp[index_temp]
// index = index_temp
// value = value_temp
// original body
// }
Сначала сделайте копию обходимого среза, получите длину, а затем используйте обычныйfor
Цикл проходится и возвращается копия значения.
посмотри сноваfor range map
Конкретная реализация:
// The loop we generate:
// var hiter map_iteration_struct
// for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
// index_temp = *hiter.key
// value_temp = *hiter.val
// index = index_temp
// value = value_temp
// original body
// }
первый справаmap
инициализируется, потому чтоmap
да*hashmap
, так что вот на самом деле*hashmap
Копия указателя.
Сочетание этих двух конкретныхfor range
Реализация компилятора, вы можете понять, почемуfor range slice
из_
оптимизация полезна, аfor range map
кстати бесполезно? Добро пожаловать, чтобы оставить сообщение, чтобы ответить.
Эта статья является оригинальной статьей, перепечатайте ее и укажите источник: «Всегда есть плохие люди, которые удаляют мое первоначальное описание, когда берут статью».
flysnow_org
или сайтwww.flysnow.org/, и впервые прочитал последующие замечательные статьи. «Замечания против гнилых людей **...&*¥» Если вы считаете, что это хорошо, поделитесь им в кругу друзей, спасибо за вашу поддержку.