【问题标题】:Is there a way to directly copy golang strings to a pre-allocated C char buffer有没有办法直接将 golang 字符串复制到预先分配的 C 字符缓冲区
【发布时间】:2021-11-23 08:53:46
【问题描述】:

我有一个可变的(但限制在组合大小)需要传递给 C 的 GoString 数量,我想尽可能便宜地做到这一点。我将多次执行此操作(因此可以将重复使用的预分配缓冲区视为零成本)。

我最初的方法是遍历 GoString,将每个转换为 CString 并将其推送到 C。

for _, str := range mystrings {
   cstr := C.CString(str)
   defer C.free(unsafe.Pointer(cstr))
   C.push_str(pushStrFn, cstr)
}

当然,由于C.CString 以及 N 个 CGo 调用,这执行了 N 个堆分配 - 所有这些都不便宜。

接下来是在 Go 中使用分配的开始时间 strings.Builder 在 Go 中构建单个大字符串,然后在单个 CGo 调用中将其与一些长度信息一起传递给 C。这是一个 CString 调用和一个 CGo 调用 - 一个实质性的改进。

builder.Reset()
for _, str := range mystrings {
   builder.WriteString(str)
}
C.push_strs(pushStrsFn, C.CString(builder.String()))

但是这种方法仍然在执行不必要的复制!理想情况下,我想预先分配一大块可以传递给 C 的内存,然后直接将字符串复制到它而不使用大型 GoString 中介。

我能够提前预分配一个大数组,并遍历 GoStrings 中的字符,一次复制一个。这避免了中间复制,但比专用的字符串复制功能(如构建器的功能)慢得多。

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 缓冲区?

【问题讨论】:

  • 你确定你的架构是正确的吗? Go-C 接口本身很慢。
  • 我知道,这很痛苦,而且根本不是我如何处理这个问题的首选,而是 c'est la vie。

标签: string go optimization cgo


【解决方案1】:

您是否尝试过C.strncpy,或者这个包装器? https://github.com/chai2010/cgo/blob/master/char.go#L61 。如果这不是更快,您可以尝试使用 unsafe 将指向 Go 字符串的指针转换为 CString 指针。

【讨论】:

  • 不幸的是,C.strncpy 包装器本身正在进行 CGo 调用,这是我在这里试图避免的更大瓶颈之一。话虽如此,通过foo := (*C.char)(unsafe.Pointer(&amp;[]byte(builder.String())[0])) 的 GoString 演员阵容非常出色!看起来我的端到端减少了大约 300ns,这太棒了。
  • @Locke:请注意,您的解决方案取决于strings.Builder 的内部实现细节,不能保证安全甚至工作。或许bytes.Buffer 更适合您,但您仍然会遇到在将指针发送到 C 代码后支持数组变得无效的问题,因此管理该缓冲区的生命周期至关重要。
【解决方案2】:

使用 unsafe 包从 C 分配的缓冲区中创建一个字节切片。

然后,您可以直接复制到该切片中,或者将其作为零长度重新切片粘贴到 bytes.Buffer 中并使用 WriteString。您必须小心不要超出原始容量,否则缓冲区会将您的数据复制到新的后备缓冲区。

【讨论】:

    猜你喜欢
    • 2012-07-17
    • 2015-10-29
    • 1970-01-01
    • 2018-08-16
    • 2013-02-09
    • 1970-01-01
    • 2012-08-15
    • 2021-12-31
    • 2011-08-14
    相关资源
    最近更新 更多