修改数组 vs seq 的数据并将数组 vs seq 的地址传递给 asyncnet proc `send`
Modifying the data of an array vs seq and passing the address of an array vs seq to asyncnet proc `send`
我一直在研究希望通过缓冲区接收数据的服务器。我有一个这样定义的对象和一些修改其中缓冲区的过程:
Packet* = ref object
buf*: seq[int8]
#buf*: array[0..4096, int8]
pos*: int
proc newPacket*(size: int): Packet =
result = Packet(buf: newSeq[int8](size))
#result = Packet()
proc sendPacket*(s: AsyncSocket, p: Packet) =
aSyncCheck s.send(addr(p.buf), p.pos)
现在我有两行评论的原因是因为那是我最初使用的代码,但是创建一个每次都初始化一个包含 4096 个元素的数组的对象可能对性能来说不是很好。但是,它有效,而 seq[int8] 版本无效。
奇怪的是,如果我使用旧的静态缓冲区 buf*: array[0..4096, int8]
,我当前的代码将工作得很好。在 sendPacket
中,我确保检查缓冲区中包含的数据以确保数组和 seq[int8] 版本相等,而且它们是相等的。 (或者至少看起来是)。换句话说,如果我要执行 var p = createPacket(17)
并用恰好 17 个字节写入 p.buf
,则元素的值在两个版本中看起来是相同的。
所以尽管两个版本中的数据看起来相同,但在传递缓冲区地址时调用 send
时我得到了不同的结果。
以防万一,数据会这样读:
result = p.buf[p.pos]
inc(p.pos)
并这样写:
p.buf[p.pos] = cast[int8](value)
inc(p.pos)
我调查过的一些事情可能与我的问题无关:我查看了 GC_ref
和 GC_unref
,它们对我的问题没有影响,并且还查看了 maybe尝试使用 alloc0
,其中 buf 被定义为 pointer
,但我似乎无法访问该指针的数据,这可能不是我首先应该做的。此外,如果我执行 var data = p.buf
并传递 data
的地址,我会得到不同的结果,但仍然不是预期的结果。
所以我想我想弄清楚的是:
- 为什么当我使用
array[0..4096, int8]
而不是使用 newSeq
初始化的 seq[int8]
时 send
工作得很好,即使它们看起来包含相同的数据?
- 我当前用于接收和写入数据的布局在像 Nim 这样的语言(或与此相关的任何语言)中是否有意义?有没有更好的办法?
为了不初始化数组,您可以像这样使用 noinit
pragma:
buf* {.noinit.}: array[0..4096, int8]
您可能正在使用指向 seq 的指针,而不是指向 seq 中数据的指针,因此请尝试使用 addr(p.buf[0])
。
如果您使用的是 seq
版本,pos
字段是无用的,因为您已经有 p.buf.len
,但您可能已经知道并只是将其留在数组中。如果你想使用 seq
并期望大数据包,请确保使用 newSeqOfCap
只分配一次内存。
此外,您的数组太大了 1 个字节,它从 0 到 4096(含)!相反,您可以使用 [0..4095, int8]
或仅使用 [4096, int8]
.
我个人更喜欢在 buf 中使用 uint8 类型,这样你就可以输入 0 到 255 之间的值,而不是 -128 到 127
在 ref 对象内部使用 seq 意味着在访问 buf
时有两层间接访问,以及 GC 必须清理的两个对象。您可以将 Packet
作为 seq[uint8]
的别名(不带 ref):type Packet* = seq[uint8]
。或者,如果您想稍后在 Packet
中存储更多数据,则可以使用数组版本。
我一直在研究希望通过缓冲区接收数据的服务器。我有一个这样定义的对象和一些修改其中缓冲区的过程:
Packet* = ref object
buf*: seq[int8]
#buf*: array[0..4096, int8]
pos*: int
proc newPacket*(size: int): Packet =
result = Packet(buf: newSeq[int8](size))
#result = Packet()
proc sendPacket*(s: AsyncSocket, p: Packet) =
aSyncCheck s.send(addr(p.buf), p.pos)
现在我有两行评论的原因是因为那是我最初使用的代码,但是创建一个每次都初始化一个包含 4096 个元素的数组的对象可能对性能来说不是很好。但是,它有效,而 seq[int8] 版本无效。
奇怪的是,如果我使用旧的静态缓冲区 buf*: array[0..4096, int8]
,我当前的代码将工作得很好。在 sendPacket
中,我确保检查缓冲区中包含的数据以确保数组和 seq[int8] 版本相等,而且它们是相等的。 (或者至少看起来是)。换句话说,如果我要执行 var p = createPacket(17)
并用恰好 17 个字节写入 p.buf
,则元素的值在两个版本中看起来是相同的。
所以尽管两个版本中的数据看起来相同,但在传递缓冲区地址时调用 send
时我得到了不同的结果。
以防万一,数据会这样读:
result = p.buf[p.pos]
inc(p.pos)
并这样写:
p.buf[p.pos] = cast[int8](value)
inc(p.pos)
我调查过的一些事情可能与我的问题无关:我查看了 GC_ref
和 GC_unref
,它们对我的问题没有影响,并且还查看了 maybe尝试使用 alloc0
,其中 buf 被定义为 pointer
,但我似乎无法访问该指针的数据,这可能不是我首先应该做的。此外,如果我执行 var data = p.buf
并传递 data
的地址,我会得到不同的结果,但仍然不是预期的结果。
所以我想我想弄清楚的是:
- 为什么当我使用
array[0..4096, int8]
而不是使用newSeq
初始化的seq[int8]
时send
工作得很好,即使它们看起来包含相同的数据? - 我当前用于接收和写入数据的布局在像 Nim 这样的语言(或与此相关的任何语言)中是否有意义?有没有更好的办法?
为了不初始化数组,您可以像这样使用 noinit
pragma:
buf* {.noinit.}: array[0..4096, int8]
您可能正在使用指向 seq 的指针,而不是指向 seq 中数据的指针,因此请尝试使用 addr(p.buf[0])
。
如果您使用的是 seq
版本,pos
字段是无用的,因为您已经有 p.buf.len
,但您可能已经知道并只是将其留在数组中。如果你想使用 seq
并期望大数据包,请确保使用 newSeqOfCap
只分配一次内存。
此外,您的数组太大了 1 个字节,它从 0 到 4096(含)!相反,您可以使用 [0..4095, int8]
或仅使用 [4096, int8]
.
我个人更喜欢在 buf 中使用 uint8 类型,这样你就可以输入 0 到 255 之间的值,而不是 -128 到 127
在 ref 对象内部使用 seq 意味着在访问 buf
时有两层间接访问,以及 GC 必须清理的两个对象。您可以将 Packet
作为 seq[uint8]
的别名(不带 ref):type Packet* = seq[uint8]
。或者,如果您想稍后在 Packet
中存储更多数据,则可以使用数组版本。