使用不安全从 golang 中的二进制数据中提取字符串的最佳方法
Best way to extract strings from binary data in golang using unsafe
我有一个加载数 GB 字节数组的应用程序。我无法控制二进制格式。该程序大部分时间都在将数组的各个部分转换为字符串,进行字符串操作,然后释放所有字符串。当有大量客户端触发内存中分配大量 objects 时,它偶尔会耗尽内存。
鉴于字节数组在应用程序的整个生命周期中都存在于内存中,这似乎是使用不安全包来避免内存分配的理想选择。
只是在 go playground 中对此进行了测试,似乎需要“SliceHeader”才能生成实际的字符串。但这意味着每次需要返回字符串时仍必须分配“SliceHeader”。 (即本例中的“x”变量)
func main() {
t := []byte{
65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76,
77, 78, 79, 80, 81, 82,
83, 84, 85,
}
var x [10]reflect.StringHeader
h := (*reflect.StringHeader)(unsafe.Pointer(&x[0]))
h.Len = 4
h.Data = uintptr(unsafe.Pointer(&t[8]))
fmt.Printf("test %v\n", *(*string)(unsafe.Pointer(&x[0])))
h = (*reflect.StringHeader)(unsafe.Pointer(&x[1]))
h.Len = 4
h.Data = uintptr(unsafe.Pointer(&t[3]))
fmt.Printf("test %v\n", *(*string)(unsafe.Pointer(&x[1])))
}
我可能会在每个客户端连接到服务器时附加一个具有固定长度字符串集 header objects 的数组(即 re-cycled 当新客户端连接时) .
这意味着 1. 字符串数据将不再被复制,并且 2. 字符串 header 不会被 allocated/garbage 收集。 3. 我们知道每台服务器的最大客户端数量,因为它们在拉出字符串时有 fixed/hardcoded 数量的字符串header可用。
我走上正轨了吗,疯了?告诉我谢谢。
使用以下函数将字节切片转换为字符串而无需分配:
func btos(p []byte) string {
return *(*string)(unsafe.Pointer(&p))
}
该函数利用了字符串 header 的内存布局是切片 header.
的内存布局前缀这一事实
调用此函数后不要修改切片的支持数组 -- 这将打破字符串不可变的假设。
像这样使用函数:
t := []byte{
65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76,
77, 78, 79, 80, 81, 82,
83, 84, 85,
}
s := btos(t[8:12])
fmt.Printf("test %v\n", s) // prints test IJKL
s = btos(t[3:7])
fmt.Printf("test %v\n", s) // prints test DEFG
我有一个加载数 GB 字节数组的应用程序。我无法控制二进制格式。该程序大部分时间都在将数组的各个部分转换为字符串,进行字符串操作,然后释放所有字符串。当有大量客户端触发内存中分配大量 objects 时,它偶尔会耗尽内存。
鉴于字节数组在应用程序的整个生命周期中都存在于内存中,这似乎是使用不安全包来避免内存分配的理想选择。
只是在 go playground 中对此进行了测试,似乎需要“SliceHeader”才能生成实际的字符串。但这意味着每次需要返回字符串时仍必须分配“SliceHeader”。 (即本例中的“x”变量)
func main() {
t := []byte{
65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76,
77, 78, 79, 80, 81, 82,
83, 84, 85,
}
var x [10]reflect.StringHeader
h := (*reflect.StringHeader)(unsafe.Pointer(&x[0]))
h.Len = 4
h.Data = uintptr(unsafe.Pointer(&t[8]))
fmt.Printf("test %v\n", *(*string)(unsafe.Pointer(&x[0])))
h = (*reflect.StringHeader)(unsafe.Pointer(&x[1]))
h.Len = 4
h.Data = uintptr(unsafe.Pointer(&t[3]))
fmt.Printf("test %v\n", *(*string)(unsafe.Pointer(&x[1])))
}
我可能会在每个客户端连接到服务器时附加一个具有固定长度字符串集 header objects 的数组(即 re-cycled 当新客户端连接时) .
这意味着 1. 字符串数据将不再被复制,并且 2. 字符串 header 不会被 allocated/garbage 收集。 3. 我们知道每台服务器的最大客户端数量,因为它们在拉出字符串时有 fixed/hardcoded 数量的字符串header可用。
我走上正轨了吗,疯了?告诉我谢谢。
使用以下函数将字节切片转换为字符串而无需分配:
func btos(p []byte) string {
return *(*string)(unsafe.Pointer(&p))
}
该函数利用了字符串 header 的内存布局是切片 header.
的内存布局前缀这一事实调用此函数后不要修改切片的支持数组 -- 这将打破字符串不可变的假设。
像这样使用函数:
t := []byte{
65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76,
77, 78, 79, 80, 81, 82,
83, 84, 85,
}
s := btos(t[8:12])
fmt.Printf("test %v\n", s) // prints test IJKL
s = btos(t[3:7])
fmt.Printf("test %v\n", s) // prints test DEFG