【问题标题】:Sized Data Load in Golang (Getting uint16 into a uint8 slice)Golang 中的大小数据加载(将 uint16 放入 uint8 切片)
【发布时间】:2016-07-21 16:15:42
【问题描述】:

我正在编写一个粗略的 ISS 处理器,我想知道是否有更有效的方法来处理我正在做的事情(最好不要求助于不安全的库)。 (简化)我通过以下方式表示记忆:

type DataMem []uint8

注册人:

type Register uint16

内存需要以字节大小为单位,并且处理器以上述更大的单位工作。这意味着存储我们所做的数据:

func (m *Machine) ExecuteST (ins Instruction) {
  // Store to memory
  // Data to store specified in one register
  // Address to store to specified by another register
  target_address := m.RegBank[ins.Dst]
  src_data := m.RegBank[ins.Src]

  ///////// Start of annoying section////////
  var target_0 uint8
  var target_1 uint8
  target_0 =  src_data & 0x0f
  target_1 = (src_data & 0xf0)>>8
  m.data_mem[target_address  ] = target_0
  m.data_mem[target_address+1] = target_1
  /////////// End of annoying section /////////
  m.logger.Printf("ST To Address %x, Data %x\n",target_address,src_data)
}

等等各种不同的数据类型和传输。

对我来说烦人的是我知道运行 go 代码的处理器将有一个加载指令,可以在一次传输中完成上述所有操作。我希望有一个好的 c 编译器可以为我优化,但不破坏不安全的库并直接指向我认为我无法解决的指针?

我愿意接受建议,包括更好的数据存储器建模方法...

【问题讨论】:

  • 我不知道有什么更好的方法来做你正在做的事情,但你的面具是错误的。第一个应该是0xff,另一个应该是0xff00
  • 糟糕,谢谢!我重新编写了我正在运行的代码以简化问题 - 看起来我简化不正确/太快了!

标签: go


【解决方案1】:

由于 Go 的类型安全性,在不使用 unsafe 包的情况下,您在这里没有什么不同。

最简单的解决方案,也可能是最高效的解决方案是对目标地址进行转换:

// convert the target address to *uint16
srcData = uint16(0xeeee)
*(*uint16)(unsafe.Pointer(&dataMem[targetAddress])) = srcData

同样,另一种选择是使用指向同一内存区域的[]uint16 切片来遮蔽[]uint8 切片。这看起来更加毛茸茸,您必须自己检查偏移量和对齐方式。

dataMem16 := (*(*[1<<30 - 1]uint16)(unsafe.Pointer(&dataMem[0])))[:len(dataMem)/2 : len(dataMem)/2]
dataMem16[targetAddress/2] = srcData

另一个尝试的选项是使用内置的copy 来移动字节。由于复制是由编译器在汇编中实现的,它可能会做你想要的(虽然我还没有检查转换的汇编实际产生了什么,所以它可能是一个清洗)

copy(dataMem[targetAddress:], (*(*[2]uint8)(unsafe.Pointer(&srcData)))[:])

或者作为@OneOfOne 显示的内联函数:

func regToMem(reg uint16) *[2]uint8 {
    return (*[2]uint8)(unsafe.Pointer(&reg))
}

copy(mem[targetAddress:], regToMem(0xffff)[:])

https://play.golang.org/p/0c1UywVuzj

如果性能很关键,并且您想使用特定于体系结构的指令,那么“正确”的方法是直接在汇编中实现您想要的,但是从第一个解决方案生成的代码可能很难被击败.

【讨论】:

  • 该死的,吉姆,别再忍着我的答案了!任何人play.golang.org/p/C8jo2Jk59R
  • @OneOfOne:谢谢,寻找第一个案例会更好
  • 非常感谢这个 TBH 我更喜欢切片阴影,因为它应该可以在没有任何在移动之上添加额外操作的风险的情况下工作 - 复制可能会添加其他东西。但是一个有用的工具可以添加到技巧包中。
  • @ChrisHopkins:我可能错过了最简单的解决方案,将目标地址转换为 uint16 而不是复制 2 个字节。它本质上与转换整个切片相同,但我认为它更不容易出错并且更容易推理。我只会使用最后一个选项,您必须在该内存区域内进行大量随机访问。
【解决方案2】:

因为我关心的是模拟的效率,所以另一个选择是以不同的方式声明数据存储器,例如

type DataMem []uint32

提供给定地址的方法提取字节

func (dm *DataMem) StoreB (address int) uint8 {...}
func (dm *DataMem) Store16 (address int) uint16 {...}

鉴于这是在 PC 上运行,那里会有一个数据缓存,因此在现代处理器上获取太大的数据字不会花费任何成本。

我需要一个新的基本规则,当您遇到棘手的问题时,请查看数据结构和您的平台;-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-22
    • 2013-02-10
    • 1970-01-01
    • 2017-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多