我有一个变量(但大小受限)数量的GoStrings需要传递给C,我希望尽可能地节省成本。 我将执行此操作多次(因此可以认为预分配重复使用的缓冲区的成本为零)。
我的初始方法是循环遍历GoStrings,将每个转换为CString并将其推送到C。
for _, str := range mystrings {
cstr := C.CString(str)
defer C.free(unsafe.Pointer(cstr))
C.push_str(pushStrFn, cstr)
}
当然,这需要使用C.CString
进行N次堆分配以及N次CGo调用,这些操作都不是很便宜。
接下来的做法是在Go中使用一个一开始就被分配的strings.Builder
构建一个单独的大字符串,并通过一个CGo调用将其传递给C语言并附带长度信息。这样只需要进行一次CString调用和一次CGo调用,大大提升了性能。
builder.Reset()
for _, str := range mystrings {
builder.WriteString(str)
}
C.push_strs(pushStrsFn, C.CString(builder.String()))
但这种方法仍然进行了不必要的复制!理想情况下,我想预先分配一个大块内存,可以将其传递给C,并直接将字符串复制到其中,而无需使用大的GoString中介。
我能够预先分配一个大数组,并遍历GoStrings中的字符,逐个复制它们。这避免了中间复制,但比专用的字符串复制函数(如builder的函数)要慢得多。
cCharArray := C.malloc(C.size_t(MAX_SIZE) * C.size_t(unsafe.Sizeof(uintptr(0))))
goCharArray := (*[1<<30 - 1]C.char)(cCharArray)
for _, str := range mystrings {
for i, c := range str {
goCharArray[offset+i] = C.char(c)
}
}
C.push_charArray(pushCharArrayFn, (*C.char)(cCharArray))
有没有更快的方法来完成这个任务?我是否可以将C缓冲区传递给strings.Builder
,或者直接使用字符串复制函数来操作C缓冲区?