Go 언어 모델: string 의 바 텀 데이터 구조 와 효율 적 인 조작
37466 단어 프로 그래 밍 언어
string 의 바 텀 데이터 구조
다음 예 를 통 해 보 자.
package main
import (
"fmt"
"unsafe"
)
// from: string.go GoLand IDE shift
type stringStruct struct {
array unsafe.Pointer // [len]byte
length int //
}
func main() {
test := "hello"
p := (*str)(unsafe.Pointer(&test))
fmt.Println(&p, p) // 0xc420070018 &{0xa3f71 5}
c := make([]byte, p.length)
for i := 0; i < p.length; i++ {
tmp := uintptr(unsafe.Pointer(p.array)) // unsafe
c[i] = *(*byte)(unsafe.Pointer(tmp + uintptr(i))) // uintptr
}
fmt.Println(c) // [104 101 108 108 111]
fmt.Println(string(c)) // [byte] --> string, "hello"
test2 := test + " world" // , string
p2 := (*str)(unsafe.Pointer(&test2))
fmt.Println(&p2, p2) // 0xc420028030 &{0xc42000a2e5 11}
fmt.Println(test2) // hello, world
}
string 의 연결 및 수정
+ 조작
string 형식 은 가 변 적 이지 않 은 형식 입 니 다. string 에 대한 수정 은 string 의 인 스 턴 스 를 새로 생 성 합 니 다. 효율 적 인 장면 이 라면 어떻게 수정 하 는 지 잘 고려 해 야 합 니 다.먼저 가장 긴
+
동작 을 말 해 보 세 요. 같은 예 에서 +
문자열 을 조합 하 는 어 셈 블 리 를 보 세 요.25 test2 := test + " world"
0x00000000004824d7 <+1127>: lea 0x105a2(%rip),%rax # 0x492a80
0x00000000004824de <+1134>: mov %rax,(%rsp)
0x00000000004824e2 <+1138>: callq 0x40dda0 <runtime.newobject> # newobject
0x00000000004824e7 <+1143>: mov 0x8(%rsp),%rax
0x00000000004824ec <+1148>: mov %rax,0xa0(%rsp)
0x00000000004824f4 <+1156>: mov 0xa8(%rsp),%rax
0x00000000004824fc <+1164>: mov 0x8(%rax),%rcx
0x0000000000482500 <+1168>: mov (%rax),%rax
0x0000000000482503 <+1171>: mov %rax,0x8(%rsp)
0x0000000000482508 <+1176>: mov %rcx,0x10(%rsp)
0x000000000048250d <+1181>: movq $0x0,(%rsp)
0x0000000000482515 <+1189>: lea 0x30060(%rip),%rax # 0x4b257c
0x000000000048251c <+1196>: mov %rax,0x18(%rsp)
0x0000000000482521 <+1201>: movq $0x6,0x20(%rsp)
0x000000000048252a <+1210>: callq 0x43cc00 <runtime.concatstring2> # concatstring2
현재 go
[2018.11 version: go1.11]
는 기본 x86 calling convention 에 따라 레지스터 로 인삼 을 전달 하 는 것 이 아니 라 stack 을 통 해 인삼 을 전달 하 는 것 이기 때문에 go 의 어 셈 블 리 는 c 처럼 이해 하기 쉽 지 않 지만 대충 알 아 보 는 것 +
뒤의 조작 은 문제 가 없습니다. runtime 소스 코드 의 조합 함 수 를 보 세 요.func concatstring2(buf *tmpBuf, a [2]string) string {
return concatstrings(buf, a[:])
}
// concatstrings implements a Go string concatenation x+y+z+...
// The operands are passed in the slice a.
// If buf != nil, the compiler has determined that the result does not
// escape the calling function, so the string data can be stored in buf
// if small enough.
func concatstrings(buf *tmpBuf, a []string) string {
idx := 0
l := 0
count := 0
for i, x := range a {
n := len(x)
if n == 0 {
continue
}
if l+n < l {
throw("string concatenation too long")
}
l += n
count++
idx = i
}
if count == 0 {
return ""
}
// If there is just one string and either it is not on the stack
// or our result does not escape the calling frame (buf != nil),
// then we can return that string directly.
if count == 1 && (buf != nil || !stringDataOnStack(a[idx])) {
return a[idx]
}
s, b := rawstringtmp(buf, l)
for _, x := range a {
copy(b, x) //
b = b[len(x):]
}
return s
}
runtime 의 concatstrings 실현 을 분석 하면
+
마지막 으로 buf 를 신청 하고 원래 의 string 을 buf 로 복사 한 다음 에 새로운 인 스 턴 스 를 되 돌려 줍 니 다.그러면 매번 +
작업 은 새로운 buf 신청 과 관련 되 고 그 다음 에 해당 하 는 copy 입 니 다.반복 적 으로 사용 +
하면 대량의 메모리 조작 신청 이 불가피 하고 대량의 연결 에 성능 이 영향 을 받 을 수 있다.bytes.Buffer
원본 코드 를 보면 bytes. Buffer 가 buffer 를 증가 할 때 2 배 에 따라 메모 리 를 증가 합 니 다. 빈번 한 신청 메모 리 를 효과적으로 피 할 수 있 습 니 다. 하나의 예 를 통 해 알 수 있 습 니 다.
func main() {
var buf bytes.Buffer
for i := 0; i < 10; i++ {
buf.WriteString("hi ")
}
fmt.Println(buf.String())
}
대응 하 는 byte 패키지 라 이브 러 리 함수 원본 코드
// @file: buffer.go
func (b *Buffer) WriteString(s string) (n int, err error) {
b.lastRead = opInvalid
m, ok := b.tryGrowByReslice(len(s))
if !ok {
m = b.grow(len(s)) // -> let capacity get twice as large
}
return copy(b.buf[m:], s), nil
}
// @file: buffer.go
// let capacity get twice as large !!!
func (b *Buffer) grow(n int) int {
m := b.Len()
// If buffer is empty, reset to recover space.
if m == 0 && b.off != 0 {
b.Reset()
}
// Try to grow by means of a reslice.
if i, ok := b.tryGrowByReslice(n); ok {
return i
}
// Check if we can make use of bootstrap array.
if b.buf == nil && n <= len(b.bootstrap) {
b.buf = b.bootstrap[:n]
return 0
}
c := cap(b.buf)
if n <= c/2-m {
// We can slide things down instead of allocating a new
// slice. We only need m+n <= c to slide, but
// we instead let capacity get twice as large so we
// don't spend all our time copying.
copy(b.buf, b.buf[b.off:])
} else if c > maxInt-c-n {
panic(ErrTooLarge)
} else {
// Not enough space anywhere, we need to allocate.
buf := makeSlice(2*c + n)
copy(buf, b.buf[b.off:])
b.buf = buf
}
// Restore b.off and len(b.buf).
b.off = 0
b.buf = b.buf[:m+n]
return m
}
string.join
이 함 수 는 최종 string 의 크기 를 한 번 에 신청 할 수 있 지만 모든 string 을 미리 준비 해 야 합 니 다. 이런 장면 도 효율 적 입 니 다. 하나의 예:
func main() {
var strs []string
for i := 0; i < 10; i++ {
strs = append(strs, "hi")
}
fmt.Println(strings.Join(strs, " "))
}
대응 하 는 라 이브 러 리 의 원본 코드:
// Join concatenates the elements of a to create a single string. The separator string
// sep is placed between elements in the resulting string.
func Join(a []string, sep string) string {
switch len(a) {
case 0:
return ""
case 1:
return a[0]
case 2:
// Special case for common small values.
// Remove if golang.org/issue/6714 is fixed
return a[0] + sep + a[1]
case 3:
// Special case for common small values.
// Remove if golang.org/issue/6714 is fixed
return a[0] + sep + a[1] + sep + a[2]
}
// string
n := len(sep) * (len(a) - 1) //
for i := 0; i < len(a); i++ {
n += len(a[i])
}
b := make([]byte, n)
bp := copy(b, a[0])
for _, s := range a[1:] {
bp += copy(b[bp:], sep)
bp += copy(b[bp:], s)
}
return string(b)
}
strings.Builder (go1.10+)
이 이름 을 보고 자바 의 라 이브 러 리 가 생각 났 습 니 다. 하하, 이 빌 더 는 사용 하기에 가장 편리 합 니 다. 1. 10 후에 도 입 된 것 에 불과 합 니 다.그 효율 도 2 배속 의 메모리 성장 에 나타난다. WriteString 함 수 는 slice 유형 이 append 함수 에 대응 하 는 2 배속 성장 을 이용 했다.하나의 예:
func main() {
var s strings.Builder
for i := 0; i < 10; i++ {
s.WriteString("hi ")
}
fmt.Println(s.String())
}
대응 라 이브 러 리 소스 코드
@file: builder.go
// WriteString appends the contents of s to b's buffer.
// It returns the length of s and a nil error.
func (b *Builder) WriteString(s string) (int, error) {
b.copyCheck()
b.buf = append(b.buf, s...)
return len(s), nil
}
총결산
Golang 의 문자열 처 리 는 매우 편리 합 니 다. 쓰레기 회수 와 내 장 된 언어 급 쓰기 지원 이 있어 복잡 한 문자열 작업 이 그리 번 거 롭 지 않 고 C/C++ 보다 훨씬 효율 적 입 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
셸 스 크 립 트 프로 그래 밍: sed 명령 으로 텍스트 편집red 는 온라인 편집기 의 일종 이다.그것 은 한 줄 의 내용 을 한꺼번에 처리한다.작업 중 에 한 고객 의 요 구 를 만 났 습 니 다. 기본 와 이 파이 의 ssid 이름 은 MAC 주소 의 뒷 6 자 리 를 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.