seattlerb / rubyinline

296 stars 41 forks source link

Cache isn't selective enough. #38

Closed rdebath closed 8 years ago

rdebath commented 8 years ago

I've quickly thrown together the script below; it's probably the most contrary POC example I can build.

The first time I run it it's perfect; it loads the program defined on the command line converts it to C and runs the function created.

The second time it doesn't run the function I ask for; instead it runs the function that I requested the first time. It looks like you're not hashing the C source to get the name of the shared object.

BTW: Moving the body of the code into the prefix successfully works around the problem in this case, but I have to leave a stub bfprog function in the main builder call.

NOTE: On a related note, what do you thing __DATE__ and __TIME__ should do?

#!/usr/bin/ruby
require "inline"
class BFCode
    inline do |builder|

        h = Hash.new{|_, k|
            (k[0] == "+") ? "m[p]+="+k.length.to_s+";\n" :
            (k[0] == "-") ? "m[p]-="+k.length.to_s+";\n" :
            (k[0] == ">") ? "p+="+k.length.to_s+";\n" :
            (k[0] == "<") ? "p-="+k.length.to_s+";\n" :
            (k[0..3] == "[-]+") ? "m[p]="+(k.length-3).to_s+";\n" :
            "#{k}\n" };
        h['[-]'] = "m[p]=0;\n";
        h['>'] = "p+=1;\n";
        h['<'] = "p-=1;\n";
        h['+'] = "m[p]+=1;\n";
        h['-'] = "m[p]-=1;\n";
        h['['] = "while(m[p]!=0){\n";
        h[']'] = "}\n";
        h['.'] = "putchar(m[p]);\n";
        h[','] = "m[p]=getchar();\n";

        bfcode= "void bfprog() {\n" +
                "static unsigned char m[100000]; register int p=0;\n" +
                ARGF.read.
                gsub(/[^\[\]+,-.<>]/,'').
                gsub(/\[\-\]\+*|\++|-+|>+|<+|./,h) +
                "}\n";

        builder.include "<stdio.h>"
        builder.c bfcode
    end
end

BFCode.new().bfprog();
zenspider commented 8 years ago

I can't reproduce with just the code above as I don't know what the input should be at all.

I have no idea what you're referring to wrt __DATE__ or __TIME__ or how it relates to this issue.

rdebath commented 8 years ago

And here was I thinking that that silly little programming language was well known.

Here's some test programs https://github.com/rdebath/Brainfuck/tree/master/testing

This is a "hello world" https://github.com/rdebath/Brainfuck/blob/master/testing/Hello.b

And this will print a different message: https://github.com/rdebath/Brainfuck/blob/master/testing/Cellsize3.b

The point with the __TIME__ define is that it changes every time you compile the C code, so, in theory, it's wrong to do any caching of the compiled code. Except, this raises the question of when the code is "supposed" look like it's been compiled. Maybe it's every time it's run, maybe it's at the modified time of the Ruby host code; maybe something different. The program ccache has a similar problem and it's take is that it ignores __TIME__ but actually converts __DATE__ to todays date before it hashes ... usually.

Oh, and for comparison (expected results) this is a tiny interpreter without any caching or other weirdness.

#!/usr/bin/ruby
eval 'm=Hash.new(p=0);'+ARGF.read.gsub(
        /./,
        '>' => 'p+=1;',
        '<' => 'p-=1;',
        '+' => 'm[p]+=1;',
        '-' => 'm[p]-=1;',
        '[' => '(',
        ']' => ')while((m[p]&=255)!=0);',
        '.' => 'putc m[p];',
        ',' => 'm[p]=STDIN.getbyte if !STDIN.eof;')
zenspider commented 8 years ago

Again, I have no idea what you're talking about wrt __DATE__ and __TIME__. Without context, I'm going to have to assume it is unrelated and ignore that aspect of this ticket.

rdebath commented 8 years ago

Okay, I'm not sure where the problem is with my description of the __TIME__ and __DATE__ macros so I'll start from the top.

The __TIME__ token in C is a standard predefined macro that the preprocessor expands into a string that describes the time that the preprocessor is being run. Because of this it is converted to a different string almost every time the C code is compiled. Ruby inline seems to be specified such that every time the function is loaded it should be compiled. This means the time returned by the __TIME__ macro should be something like the current time that the ruby script starts running.

However, caching the binaries changes this. The time that is "returned" by the __TIME__ macros now gets frozen as the time that the particular function was first run, even if this was a very long time ago.

As an example consider the code below; as it stands with the first builder.prefix line commented out this program prints the time that it's first run. On that first run the binary is cached and the fact that the __TIME__ changes is ignored. But if you add a comment to the C code by uncommenting the first prefix line the behaviour changes. The C code now prints out the time that it's run because the code is recompiled every time because the comment changes.

So the question is some where around there; is it a problem that changing a comment changes the behaviour of the code ? (In this admittedly weird case) Is it reasonable that __TIME__ and __DATE__ get frozen between runs? Or perhaps should the existence of a __TIME__ macro in the C code change the caching behaviour in some way?

#!/usr/bin/ruby
require "inline"
require "time"
class Testcode
    inline do |builder|
        builder.include "<stdio.h>"
#       builder.prefix '/*' + DateTime.now.strftime("%T") + '*/'
        builder.prefix "#define T __TIME__"
        builder.c "void prog() { printf(\"%s\\n\", T); }"
    end
end

Testcode.new().prog();
zenspider commented 8 years ago

Ruby inline seems to be specified such that every time the function is loaded it should be compiled.

This is false. If there is something that says that somewhere, it is an error in documentation.

zenspider commented 8 years ago

From the readme:

Inline allows you to write foreign code within your ruby code. It automatically determines if the code in question has changed and builds it only when necessary.

zenspider commented 8 years ago

No response in a month... closing.

rdebath commented 8 years ago

You've been distracted by the 'Note' about __TIME__ on the first post, while I'm not 100% sure that "builds only when necessary" says exactly what you want it too I'm happy leaving it.

However, you have the interpreter (and now example programs for it) that have a problem because you only determine if code is different based on the ruby source, not the C source that's actually being compiled.