Производит ли фрагмент строки копирование базовых данных? - PullRequest
0 голосов
/ 19 сентября 2018

Я пытаюсь эффективно считать руны из utf-8 string, используя библиотеку utf8 .Является ли этот пример оптимальным в том смысле, что он не копирует основные данные?
https://golang.org/pkg/unicode/utf8/#example_DecodeRuneInString

func main() {
    str := "Hello, 世界" // let's assume a runtime-provided string
    for len(str) > 0 {
        r, size := utf8.DecodeRuneInString(str)
        fmt.Printf("%c %v\n", r, size)
        str = str[size:] // performs copy?
    }
}

Я обнаружил StringHeader в (небезопасной) библиотеке отражений.Это точная структура string в Go?Если это так, вполне возможно, что нарезка строки просто обновляет Data или выделяет новый StringHeader в целом.

type StringHeader struct {
        Data uintptr
        Len  int
}

Bonus : где я могу найти код, который выполняет string нарезка, чтобы я сам мог посмотреть?Любой из них?
https://golang.org/src/runtime/slice.go
https://golang.org/src/runtime/string.go

Этот связанный с SO ответ предполагает, что строки времени выполнения имеют копию при преобразовании из string в []byte.

1 Ответ

0 голосов
/ 19 сентября 2018

Slicing Strings

выполняет ли фрагмент строки копирование базовых данных?

Нет, это не так.См. Эту статью Расс Кокс:

Строка представлена ​​в памяти как структура из двух слов, содержащая указатель на строковые данные и длину.Поскольку строка является неизменной, для нескольких строк безопасно совместно использовать одно и то же хранилище, поэтому разделение s приводит к новой структуре из двух слов с потенциально другим указателем и длиной, которая по-прежнему ссылается на одну и ту же последовательность байтов.Это означает, что срезы можно выполнять без выделения или копирования, что делает строковые срезы такими же эффективными, как и передача явных индексов.

- Структуры данных Go

Срезы, Производительность и итерации по рунам

Срез - это три вещи: длина, емкость и указатель на местоположение в базовом массиве.

Таким образом, сами срезы не являютсяочень большой: целые и указатель (возможно, некоторые другие мелочи в деталях реализации).Таким образом, выделение, необходимое для создания копии среза, очень мало и не зависит от размера базового массива.И никакого нового выделения не требуется, когда вы просто обновляете длину, емкость и местоположение указателя, например, в строке 2:

foo := []int{3, 4, 5, 6}
foo = foo[1:]

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

Строки в Go неизменны.Таким образом, чтобы изменить строку, вам нужно создать новую строку.Однако строки тесно связаны с байтовыми слайсами, например, вы можете создать байтовый фрагмент из строки с помощью

foo := `here's my string`
fooBytes := []byte(foo)

Я считаю, что это выделит новый массив байтов, потому что:

строка в действительности является доступным только для чтения срезом байтов

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

Вы можете использовать профилирование производительности и бенчмаркинг , чтобы получить более полное представление о производительности вашей программы.

Как только вы получите свой кусочек байтов, fooBytes, пересчитываяон не выделяет новый массив, он просто выделяет новый фрагмент, который является маленьким.Похоже, это то же самое, что и секция строки.

Обратите внимание, что вам не нужно использовать пакет utf8 для подсчета слов в строке utf8, хотя вы можете продолжить этот путь, если хотите.Go обрабатывает utf8 изначально.Однако, если вы хотите перебрать символы, вы не можете представить строку в виде фрагмента байтов, поскольку у вас могут быть многобайтовые символы.Вместо этого вам нужно представить его как кусочек руны:

foo := `here's my string`
fooRunes := []rune(foo)

Эта операция преобразования строки в кусочек руны в моем опыте быстрая (тривиально в тестах, которые я сделал, но может бытьраспределение).Теперь вы можете перебирать fooRunes для подсчета слов, пакет utf8 не требуется.В качестве альтернативы, вы можете пропустить явное преобразование []rune(foo) и сделать это неявно, используя цикл for ... range для строки, потому что они являются специальными:

A для цикла диапазона, напротив, декодирует одинUTF-8-кодированная руна на каждой итерации.Каждый раз вокруг цикла индекс цикла - это начальная позиция текущей руны, измеренная в байтах, а кодовая точка - ее значение.

- Строки, байты,руны и символы в Go

...