【问题标题】:Creating a static array class in ruby with ruby FFI使用 ruby​​ FFI 在 ruby​​ 中创建静态数组类
【发布时间】:2019-04-08 10:17:42
【问题描述】:

我想在 ruby​​ 中实现我自己的静态数组类。它将是一个具有固定容量的数组,并且数组中的所有元素都是单一类型。 为了直接访问内存,我使用了 FFI gem https://github.com/ffi/ffi,它可以创建您自己的 C 函数并在您的 ruby​​ 程序中使用它们。 我创建了一个非常简单的 C 函数,它为整数数组分配内存并返回指向内存空间的指针:

int * create_static_array(int size) {
  int *arr = malloc(size * sizeof(int));
  return arr;
}

这是我使用 create_static_array 的 ruby​​ static_array 类:

require 'ffi'
class StaticArray
  attr_accessor :pointer, :capacity, :next_index
  extend FFI::Library

  ffi_lib './create_array/create_array.so'
  attach_function :create_static_array, [:int], :pointer

  def initialize(capacity)
    @capacity = capacity
    @pointer = create_static_array(capacity)
    @next_index = 0
  end
 # adds value to the next_index in array
  def push(val)
    @pointer[@next_index].write(:int, val)
    @next_index += 1
  end
 # reads value at index
  def [](index)
    raise IndexOutOfBoundException if index >= @capacity
    self.pointer[index].read(:int)
  end
 # print every value in index
  def print
    i = 0
    while (i <   @capacity)
      puts @pointer[i].read(:int)
      i += 1
    end
  end
end

我添加了几个方法来与我的数组交互、推送元素、读取索引处的元素... 但是我的 static_array 实例并没有按预期工作......

假设我在写:

// creates a static array in memory which can store 4 ints
arr = StaticArray.new(4)

现在让我们在我们的 arr 中推入一个 int :

arr.push(20)

arr.print 将输出

20
0
0
0

这是有道理的。现在让我们将另一个 int 推入 arr :

arr.push(16)

arr.print 再次:

4116
16
0
0

20 已被 4116 取代......我真的不明白这里发生了什么?

Here's the link to the FFIPointer class doc if that helps https://www.rubydoc.info/github/ffi/ffi/FFI/Pointer

【问题讨论】:

  • 你的目标是什么?性能、减少内存使用、与其他本机代码的交互?

标签: c arrays ruby memory ffi


【解决方案1】:

FFI 接口不知道指针的类型,因此它只是将其视为字节数组(请参阅指针类型的初始化)。请注意,虽然您确实传递了:int,但这是针对特定的writeread,而不是您正在执行索引的位置。因此,您在字节偏移量 0、1、2、3 处写入和打印,而不是在 0、4、8、12 处的整数元素。

在 little endian 系统上,具有 32 位、4 字节的 int,二进制值 20 是 14 00 00 00,而 16 是 10 00 00 00

所以你分配了 4*4 字节,所以 32 字节,前 8 个字节。

00 00 00 00 00 00 00 00

并在偏移量 0 处写入 20

14 00 00 00 00 00 00 00

然后在偏移量 1 处写入 16

14 10 00 00 00 00 00 00

14 10 00 000x00001014 或 4116,然后在下一个偏移处打印它是 10 00 00 00,即 16。

【讨论】:

  • 所以我初始化了一个 16 字节的数组,我的指针不知道我初始化的指针类型,所以当我试图通过索引访问特定值时,我实际上是在访问一个特定的字节。然后因为我指定了我想要读取的值的类型 (read(:int)),所以指针知道我想要从那里读取所有 4 个字节并计算一个整数。根据您的解释,我知道要使我的程序正常运行,我应该将所有索引乘以 4。Github repo
  • 但是我仍然得到了一些意想不到的行为......例如:arr = StaticArray.new(4) arr.print 0 -268435456 -1600786800 2047我在这里做错了什么?
  • malloc 不保证内存是零填充的,并且可能会从以前的内存使用中留下一些东西。看起来可能是这样。
  • 所以我不明白 malloc 的用途......如果当你使用 malloc(n) 1) 你不能保证你有 n 的可用空间和 2) 你实际上并没有引发错误如果您尝试写入比您使用 malloc 影响的偏移量更远的偏移量(C 不提供任何处理访问无效索引问题的规范)。那么使用 malloc 与初始化随机指针相比有什么优势......
  • 1) 如果malloc(n) 返回一个非空指针,则可以保证(大多数情况下,参见Linux OOM 杀手),您有n 个字节。但出于性能原因(使用 C 的常见原因之一),它不承诺将这些字节设置为任何特定值(例如 0)。如果您想将其归零,可以使用 memset。出于安全原因,这些天来自操作系统的新鲜(尚未被特定进程使用)被归零(所以你看不到它是否有来自说 Chrome 的密码),但是在一个进程创建/销毁一些之后malloc 将重用充满东西的内存。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-29
  • 1970-01-01
  • 2012-07-10
  • 1970-01-01
相关资源
最近更新 更多