针对 Ruby 优化的位交错
诚然,在 Ruby 中优化位操作一开始就有点不匹配。除此之外,我正在寻找一个片段或一个 gem,可以交错两个任意整数坐标,最好可以用于 MRI (1.9) 或本地 gem。
C 中的一些方法是: http://graphics.stanford.edu/~seander/bithacks .html#InterleaveTableObvious
作为示例或起点,这里是 Ruby 中的“Interleave 位明显方式”,有点丑陋,以防止它创建临时数组(这会增加每个数组的运行时间约 2 倍)并使用二进制文件内联长度方法可进一步减少 6%(如果您知道两个输入都不为零,则可以省略该检查,再增加几个百分点。)
def interleave(y)
z = 0
bl = self > 0 ? Math.log2(self) : 1
ybl = y > 0 ? Math.log2(y) : 1
((((bl <=> ybl) == -1) ? ybl : bl).floor + 1).times{|i| z |= (self & 1 << i) << i | (y & 1 << i) << (i + 1)}
return z
end
来自 2.66Ghz i5 和 1.9.2p180 的结果:
x = y = 0b11111111_11111111_11111111_11111111
Benchmark.bm{|bm| bm.report{1000000.times{x.interleave(y)}}}
user system total real
18.360000 0.010000 18.370000 ( 18.356196)
肯定有更好的方法吗?
更新
我包括了@Wayne Conrad 的零修复,尽管比他的丑陋得多,而且速度也只是稍微快一点。还移动了楼层和+1,以便每次执行一次而不是两次。
这是匹配去交错的要点。
Granted, optimizing bit twiddling in Ruby is a bit of a mismatch to begin with. That aside, I'm looking for a snippet or a gem that can interleave two arbitrary integer coords optimized as best can be for MRI (1.9) or a native gem.
Some approaches in C are: http://graphics.stanford.edu/~seander/bithacks.html#InterleaveTableObvious
As an example or starting point, here's "Interleave bits the obvious way" in Ruby, somewhat uglified to keep it from creating temp arrays (which increase the runtime by about 2X per array) and with a binary length method inlined for a further 6% decrease (If you know neither input is ever zero, you can omit that check for a few percent more..)
def interleave(y)
z = 0
bl = self > 0 ? Math.log2(self) : 1
ybl = y > 0 ? Math.log2(y) : 1
((((bl <=> ybl) == -1) ? ybl : bl).floor + 1).times{|i| z |= (self & 1 << i) << i | (y & 1 << i) << (i + 1)}
return z
end
Results from a 2.66Ghz i5 with 1.9.2p180:
x = y = 0b11111111_11111111_11111111_11111111
Benchmark.bm{|bm| bm.report{1000000.times{x.interleave(y)}}}
user system total real
18.360000 0.010000 18.370000 ( 18.356196)
Surely there's a better way?
Update
I included the zero fix from @Wayne Conrad, albeit far uglier than his and only marginally faster. Also moved the floor and + 1 so as to be executed once instead of twice per.
Here is a Gist of this with matching de-interleave.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是一个快速&俗气的实现让你继续前进,直到出现一个好的实现:
正如你所料,它不是速度守护进程。在我的机器上,使用 MRI 1.8.7,它每秒计算大约 35,000 个 16 位结果。您的每秒计算 68,000 个 16 位结果。或者,请参阅每秒 256,000 个 16 位结果的下一个算法。
如果您愿意牺牲一点内存和启动时间来换取速度,那么:
该程序每秒计算 256,000 个 16 位结果。
如果任一参数为零,则您的答案存在错误。这是一种可能的解决方法。首先定义这个函数:
然后,在
interleave
中,将: 替换为:
这是我使用的 rspec 测试用例:
Here's a quick & cheesy implementation to get you going until a good one comes along:
As you might expect, it's no speed deamon. On my box, with MRI 1.8.7, it computes about 35,000 16-bit results per second. Yours computes 68,000 16-bit results per second. Or, see the next algorithm for 256,000 16-bit results per second.
If you're willing to trade a little memory and startup time for speed, then:
This one computes 256,000 16-bit results per second.
There's a bug in your answer if either argument is zero. Here's one possible fix for it. First define this function:
And then, inside
interleave
, replace:with:
Here is the rspec test case I used:
这是另一种解决方案,对于 16 位整数(第一个解决方案仅适用于 8 位),基准测试比公认的解决方案快 50%:
Here's another solution, benchmarked about 50% faster than the accepted one, and for 16-bit integers (where the first one only does 8-bit):
如果你已经有 C 实现,可以使用 FFI,否则可以直接借助帮助编写RubyInline
If you have an implementation already in C, you can use FFI, otherwise you can write it directly with the help of RubyInline