HNHacker News
TopNewBestAskShowJobs

matsadler

51 karma · joined September 16, 2013

submissionscomments
matsadler··on Halton Sequence
I don't know why this caught HN's attention today, but I first found out about the Halton sequence a number of years ago when I needed a random seeming, regularly distributed and stable set of 2D points to sample an image, and the Halton sequence fit the bill precisely.

After implementing and benchmarking it I found my code was spending more time calculating the sample points than I'd like. When trying to speed that up I found this paper: https://www.sciencedirect.com/science/article/pii/0898122193...

Later when learning Rust I ported that faster approach to Rust: https://crates.io/crates/halton

And when I wrote a Rust library to bind Rust to Ruby, I created a Rubygem of the same as a testbed: https://rubygems.org/gems/halton

A few years ago I also put together a fun D&D game using the Halton sequence to place items/encounters on a map.

matsadler··on Ruby 3.2.0
The template generated with bundle gem —-ext=rust uses the Magnus[1] library that provides a high-level friendly Rust wrapper over the Ruby C API, but you can also use rb-sys[2] which is lower level bindings direct to the Ruby C API.

[1]: https://github.com/matsadler/magnus [2]: https://github.com/oxidize-rb/rb-sys

matsadler··on News for Ruby 3.2.0

  Add support for bundle gem --ext=rust command.
Cool to see support for writing extension gems in Rust shipping with Ruby.
matsadler··on Tiktoken: OpenAI’s Tokenizer
https://github.com/ankane/tokenizers-ruby
matsadler··on Ruby YJIT Ported to Rust
I think the goal of this right now is just to match the C version.

The C implementation of YJIT supported x86 Unix/Linux platforms, and it sounds like adding Windows and arm64 support, plus other improvements was a daunting task with the tools C provides.

Now it’s in Rust we’ll hopefully see further improvements quicker.

matsadler··on Ruby Tips, Part 5
Here's an example that shows x ||= y behaves like x || x = y, rather than x = x || y

    class Foo
      def foo=(object)
        puts "foo= called with #{object.inspect}"
        @foo = object
      end

      def foo
        puts "foo called"
        @foo
      end
    end

    puts "x || x = y"

    a = Foo.new

    a.foo || a.foo = 1
    a.foo || a.foo = 2

    puts "\nx = x || y"

    b = Foo.new

    b.foo = b.foo || 1
    b.foo = b.foo || 2

    puts "\nx ||= y"

    c = Foo.new

    c.foo ||= 1
    c.foo ||= 2
This outputs:

    x || x = y
    foo called
    foo= called with 1
    foo called

    x = x || y
    foo called
    foo= called with 1
    foo called
    foo= called with 1

    x ||= y
    foo called
    foo= called with 1
    foo called
and you can see that the output for ||= matches the output for x || x = y
matsadler··on Ruby Tips, Part 5
For the cases I have benchmarked concatenation with << has been faster than join on an array.

Here's a simple benchmark:

    require "benchmark"

    n = 100_000
    Benchmark.bm(4) do |x|
      x.report("<<") do
        n.times do
          "aaaaa " << "bbbbbb " << "ccccc " << "ddddd " << "eeeee " << "fffff"
        end
      end

      x.report("join") do
        n.times do
          ["aaaaa", "bbbbbb", "ccccc", "ddddd", "eeeee", "fffff"].join(" ")
        end
      end
    end
The results I get for this are:

               user     system      total        real
    <<     0.140000   0.000000   0.140000 (  0.143750)
    join   0.230000   0.000000   0.230000 (  0.228035)
I'd be interested to see if there were any use cases where the relative performance was reversed.
matsadler··on What's New in Ruby 2.1 [pdf]
I've not read the code, so someone may have to correct me, but I think this is the gist of it:

Ruby 2.1 introduces a generational garbage collector, this divides all objects into young and old generations. A regular GC run will only look at the young generation, with the old being collected less frequently. An object is promoted to the old generation when it survives a young generation run.

If you have objects in the old generation referring objects in the young generation, but you're only looking at the young generation it may seem like an object doesn't have any references, and you might incorrectly GC an in-use object. Write barriers prevent this by adding old generation objects to a 'remember set' when they are modified to refer to a young generation object (eg old_array.push(young_string)). This 'remember set' is then taken in to account when collecting the young generation.

Most generational garbage collectors need these write barriers on all objects, but with the many 3rd party C extensions available for Ruby this isn't possible, so a workaround was devised whereby objects that aren't write barrier protected won't ever be promoted to the old generation. This isn't ideal as you won't get the full benefit of the generational GC, but it does maximise backwards compatibility.

matsadler··on What's New in Ruby 2.1 [pdf]
#reachable_objects_from was added in 2.0, and I think #memsize_of was introduced in one of the 1.9 releases. The documentation is new though.

I wrote a little about them here: http://globaldev.co.uk/2013/03/ruby-2-0-0-in-detail/#id6

matsadler··on Ruby Tips Part 2
You're correct, that is the default behaviour of super.

My intention wasn't to say that super only works like that when you've defined the method like `def foo( * )` but rather `def foo( * )` is an alternative to having to name your arguments when you're not even going to use them as they are automatically passed with bare super.

matsadler··on Ruby Tips Part 2
(context: I'm the author)

I do agree with you, but through some failing of my own I still quite enjoy that trick. I included it to highlight the syntax, but I'll add a note saying it's maybe not the best idea to actually use it.