2016-07-13 2 views
2

In Go, какой фрагмент выделяет меньше объектов? Или они оба используют одинаковое количество ассигнований, если это так; Зачем? (:Как работают константы в Go?

for i := 0; i < 10000000; i++ { 
     log.println("hello") 
    } 

ли ниже код выделяет только 1 строку

const (
    HELLO string = "hello" 
) 

for i := 0; i < 10000000; i++ { 
    log.println(HELLO) 
} 
+2

Я уверен, что компилятор будет встраивать строку и не выделять ее X раз. Если бы язык сделал это, я бы не использовал его. – squiguy

+1

Сравните выходы сборки обоих примеров с помощью 'go tool compile -S' –

ответ

5

Вот адаптация вашей программы, которая выводит базовое представление строки в обоих случаях

package main 

import (
    "fmt" 
    "reflect" 
    "unsafe" 
) 

const (
    Hello string = "hello" 
) 

func main() { 
    for i := 0; i < 3; i++ { 
     a := "hello" 
     sh := (*reflect.StringHeader)(unsafe.Pointer(&a)) 
     fmt.Println(a, " ", *sh) 
    } 

    fmt.Println() 

    for i := 0; i < 3; i++ { 
     a := Hello 
     sh := (*reflect.StringHeader)(unsafe.Pointer(&a)) 
     fmt.Println(a, " ", *sh) 
    } 
} 

Вот?. выход:

hello {4870353 5} 
hello {4870353 5} 
hello {4870353 5} 

hello {4870353 5} 
hello {4870353 5} 
hello {4870353 5} 

Заголовок строки в {} на выходе показывает указатель на данные символа («привет») и длину строки.

Вы можете видеть, что указатель на строковые данные одинаковый во всей программе: данные байтов «hello» ссылаются только на один адрес памяти (здесь 4870353), независимо от количества циклов, и независимо от того, жестко закодированную строку или константу.

Спецификация языка сама по себе не дает никаких гарантий относительно такого поведения, но постоянная интерполяция строк - такая естественная оптимизация, что было бы удивительно, если бы поведение пошло по-другому.

1

Самый простой способ определить, использует ли Go какие-либо выделения или нет, записывает эталон. В вашем случае код может выглядеть следующим образом:

package sof 

import "log" 

const (
    HELLO = "hello" 
) 

func forString(max int) { 
    for i := 0; i < max; i++ { 
     logMsg("hello", false) 
    } 
} 

func forConst(max int) { 
    for i := 0; i < max; i++ { 
     logMsg(HELLO, false) 
    } 
} 

func logMsg(msg string, stdOut bool) { 
    if stdOut { 
     log.Println(msg) 
    } 
} 

и бенчмарк:

package sof 

import "testing" 

func BenchmarkForString(b *testing.B) { 
    for i := 0; i < b.N; i++ { 
     forString(i) 
    } 
} 

func BenchmarkForConst(b *testing.B) { 
    for i := 0; i < b.N; i++ { 
     forConst(i) 
    } 
} 

Тогда вы можете просто запустить тест:

go test -bench=. -benchmem 

Очень важно -benchmem флаг. Без этого ваш бенчмарк расскажет вам только о контрольном времени - вы не получите информацию о количестве распределений и среднем размере распределения за операцию.

Выход бенчмарка:

testing: warning: no tests to run 
BenchmarkForString-4  100000  133551 ns/op   0 B/op   0 allocs/op 
BenchmarkForConst-4  100000  128585 ns/op   0 B/op   0 allocs/op 
PASS 
ok  .../sof 26.475s 

В последние 2 колонки у Вас есть информация о размере ассигнований и его номер. В вашем примере есть только нули, что означает, что обе funcs не делают никаких распределений:)

Смежные вопросы