我正在尝试使用具有非常大的起始索引的切片,例如mySlice
。我不想通过总是将它作为mySlice[index - mySliceStartIndex]
显式地减去起始索引,而是试图简单地定义切片,以便我可以在没有像mySlice[index]
这样的算术的情况下使用它。这可以在不为所有未使用的低指数分配内存的情况下完成吗?
这样做的天真方式,分配一个切片然后重新解析它(例如mySlice = mySlice[3*1024*1024*1024:4*1024*1024*1024]
)显然是内存效率低下的,因为底层数组不仅需要为整个范围分配,而且仍然分配。甚至不起作用,因为之后以索引3 * 1024 * 1024 * 1024的数据现在位于索引0,而我的目标是将其保持在原始索引。
我可以分配切片(或其底层数组),使切片开头以下的索引不被分配,理想情况下甚至不是最初的吗?
答案 0 :(得分:4)
如果没有实际/不分配/未分配未使用的部分,这是不可能的。
在Go中定义切片的方式是通过reflect.SliceHeader
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
它不包含起始索引字段。仅仅是对底层固定大小数组的引用。
正是这个底层数组保存了您的实际数据。切片只是该数组的“窗口”,它总是从索引0
开始。 0
可能位于基础数组中的任何位置。
例如,考虑following code:
a := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
b := a[2:8]
c := a[8:]
d := b[2:4]
这会产生如下内存布局:
fixed array: [ 0 1 2 3 4 5 6 7 8 9 ] > [10]int at address 273785072
slice a : . . . . . . . . . . > SliceHeader{Data:273785072 Len:10 Cap:10}
slice b : . . . . . . > SliceHeader{Data:273785080 Len:6 Cap:8}
slice c : . . > SliceHeader{Data:273785104 Len:2 Cap:2}
slice d : . . > SliceHeader{Data:273785088 Len:2 Cap:6}
Data
的值只是固定数组的地址偏移量,所有四个切片共享底层存储。
a =:= $273785072
b =:= $273785080 =:= $a + sizeof(int)*2 =:= $a + 8
c =:= $273785104 =:= $a + sizeof(int)*8 =:= $a + 32
d =:= $273785088 =:= $b + sizeof(int)*2 =:= $a + sizeof(int)*4 =:= $a + 16
无论您重新切片现有切片的索引如何,新切片将始终从0
索引到len(s)
,因为它指向的基础固定数组中的地址将其放在那里。 / p>
如果您的数据是从磁盘上的文件加载的,则可以使用不同的选项:使用syscall.Mmap
通过切片提供对数据的访问,从所需的索引开始。返回的切片现在是0
的索引,它仅涵盖您指定的范围。
func mmap(fd *os.File, start, size int) ([]byte, error) {
_, err := fd.Seek(0, 0)
if err != nil {
return nil, err
}
return syscall.Mmap(int(fd.Fd()), start, size,
syscall.PROT_READ, syscall.MAP_SHARED)
}
完成使用后,不要忘记在返回的切片上调用syscall.Munmap
。