Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Random to standard libraries #475

Merged
merged 6 commits into from
Jun 13, 2016
Merged

Conversation

TobiasWrigstad
Copy link
Contributor

Adds a new passive class "Random" to the standard libraries.
Each instance of Random encapsulates a seed of its own.
Technically, this may be wasteful -- possibly one seed per
thread might do, but this will facilitate debugging programs
with random numbers.

Documentation still lacking. Where should I put that?

Adds a new passive class "Random" to the standard libraries.
Each instance of Random encapsulates a seed of its own.
Technically, this may be wasteful -- possibly one seed per
thread might do, but this will facilitate debugging programs
with random numbers.
@TobiasWrigstad TobiasWrigstad added this to the June'16 Sprint milestone Jun 8, 2016
@TobiasWrigstad
Copy link
Contributor Author

@EliasC tells me there is no place for standard library documentation. Therefore, I proclaim this ready to be merged.

@supercooldave
Copy link

@TobiasWrigstad It still needs to be reviewed. No skipping steps.

-- Initialisation always with a seed
def init(seed:int) : void
embed void
_this->_enc__field_seed = (unsigned) #{seed};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of relying on _this->... = ..., is it possible to write this.seed = embed ... end. we avoid relying on the internal name

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internal names are going to change real soon, so relying on them is a bad idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is that I am storing an unsigned inside of an int64_t. Therefore I preferred to do it all at the C-level. What if the C code inserts a cast? Seems better to rely on local things only.
The other possibility is to have an embed type:

seed: embed unsigned end

but this will require adding an empty trace function. I could do that. But I would still need to do _this-> because "unsigned" is not a valid Encore type.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you only need the cast when you are passing the seed to rand_r?

@TobiasWrigstad
Copy link
Contributor Author

So,

  def init(seed:int) : void
    embed void
      _this->_enc__field_seed = (unsigned) #{seed};
    end

translates into

  ({_this->_enc__field_seed = (unsigned) _enc__arg_seed;});

whereas

  def init(seed:int) : void
    this.seed = embed int
      (unsigned) #{seed};
    end

translates into

  int64_t _embed_0 = ({(unsigned) _enc__arg_seed;
   });
  (*({ _this;}))._enc__field_seed = _embed_0;

I must confess that it is somewhat unclear to me what the consequences are of casting a signed int to an unsigned into and then into a signed int again, in this scenario. Does anyone have an idea?

I suspect this matters little in the end, as long as the behaviour of assigning seed N is consistent.

@TobiasWrigstad
Copy link
Contributor Author

@supercooldave I don't think relying on internal names is a big problem — I suspect this is the module system messing with things? This is an easy fix once the changes are in.

@TobiasWrigstad
Copy link
Contributor Author

@supercooldave Sorry, I meant ready to be reviewed. Precisely I mean, "I feel it can be merged to someone should review it".

@TobiasWrigstad
Copy link
Contributor Author

@kikofernandez Can the commits be squashed during merge, or should I do that some other way?

@kikofernandez
Copy link
Contributor

@TobiasWrigstad do you experience this same issue?

>>> FAILED TEST: random:
     | running out test...
     | encore/stdlib/random
     | Error: Module Random found in multiple places:
     | -- /Users/kikofernandezreyes/Code/encore/bundles/standard/Random.enc
     | -- ./Random.enc
     | Unable to determine which one to use.
     | ERROR: encore/stdlib/random.enc should compile.

@TobiasWrigstad
Copy link
Contributor Author

@kikofernandez Yes. But not before. I wonder what I have changed. Could it be the random function and the Random class?

@kikofernandez
Copy link
Contributor

@TobiasWrigstad we can squash the commits, you don't have to do anything

@supercooldave
Copy link

@kikofernandez @TobiasWrigstad The problem is that you have a file called random in both the local directory and one in the standard library, so when doing the import it doesn't know which one to import -- which is obviously a bug. For now, rename the test file to randomTest.enc.

@TobiasWrigstad
Copy link
Contributor Author

I can't get rid of the error. This seems like something that @supercooldave should know how to fix/debug.

@TobiasWrigstad
Copy link
Contributor Author

Does this have something to do with the case insensitive file system on the Mac?

@supercooldave
Copy link

@TobiasWrigstad Did you see my comment? Rename the test file to randomtest.enc or similar.

@TobiasWrigstad
Copy link
Contributor Author

@supercooldave Thanks, I just saw. Test passes now!

@kikofernandez I don't know what you mean. The seed is not used in the code.

@kikofernandez
Copy link
Contributor

never mind, I thought that something like this may work:

def random(max:int) : int                                                     |
28    let seed = this.seed in                                                     |
29    embed int                                                                   |
30      rand_r(#{seed}) % #{max};                                                 |
31    end

@kikofernandez
Copy link
Contributor

but I don't know and need to embark now...

@albertnetymk
Copy link
Contributor

https://github.com/TheGrandmother/Boids/blob/master/boids1/Random.enc

We had similar attempts during the hackathon in London.

@TobiasWrigstad
Copy link
Contributor Author

@kikofernandez I see what you mean, but it won't work unless you write it like this:

def random(max:int) : int
  let
    result = 0
    seed = this.seed
  in {
    embed void
      #{result} = (int64_t) rand_r(&#{seed});
    end ; 
    this.seed = seed ;
    result 
  }

because there is a side-effect on seed that must be preserved or else we'll keep returning the same number. Do you think that code is preferable? There's going to be a slight performance degradation, but not much. Possibly none under -O3.

@EliasC Does inference work with embed types?

@albertnetymk — feel free to backport things into this PR if you think we need more bells and whistles.

@TobiasWrigstad
Copy link
Contributor Author

@kikofernandez bump. See my rewrite of your code 4 days ago.

@kikofernandez
Copy link
Contributor

@TobiasWrigstad regarding:

So,

def init(seed:int) : void
 embed void
   _this->_enc__field_seed = (unsigned) #{seed};
 end

translates into

({_this->_enc__field_seed = (unsigned) _enc__arg_seed;});

whereas

def init(seed:int) : void
  this.seed = embed int
    (unsigned) #{seed};
  end

translates into

int64_t _embed_0 = ({(unsigned) _enc__arg_seed;
 });
(*({ _this;}))._enc__field_seed = _embed_0;

I must confess that it is somewhat unclear to me what the consequences are of casting a signed int to an unsigned into and then into a signed int again, in this scenario. Does anyone have an idea?

I suspect this matters little in the end, as long as the behaviour of assigning seed N is consistent.

the thing is that we are doing the same cast in your original solution (first one above) given that the signature is:

void* _enc__method_Random__init(pony_ctx_t* _ctx, _enc__passive_Random_t* _this, int64_t _enc__arg_seed)

This makes me wonder if we should check on the limits, if (seed > 2^32) then return "error". For me, the reason for throwing the error is that a programmer may assume that he is passing a 64 bit integer when, in reality, we take the least 32 significant bits from the int64_t.

Consequence: this is not random anymore, big numbers now match small numbers and the random generator is not secure (to use in encryption apps). I think it's better to crash at runtime

@kikofernandez
Copy link
Contributor

@TobiasWrigstad regarding:

@kikofernandez I see what you mean, but it won't work unless you write it like this:

def random(max:int) : int
  let
    result = 0
    seed = this.seed
  in {
    embed void
      #{result} = (int64_t) rand_r(&#{seed});
    end ; 
    this.seed = seed ;
    result 
  }

because there is a side-effect on seed that must be preserved or else we'll keep returning the same number. Do you think that code is preferable? There's going to be a slight performance degradation, but not much. Possibly none under -O3.

I do not like it at all... if makes it difficult to read. For this reason, I think it's better to keep your current implementation

@kikofernandez
Copy link
Contributor

I think the PR is ready to merge as soon as we figure out the kind of behavior we want for the random numbers. This means, if a number is bigger than 32 bits, do we crash or do we do the cast and work on 32 bits?

@TobiasWrigstad
Copy link
Contributor Author

@kikofernandez Re the 2^32 check. Excellent suggestion! Thanks!

@TobiasWrigstad
Copy link
Contributor Author

@kikofernandez I've elected to check the seed but not the random function -- that will be too expensive in benchmarks that hit random a lot. Are you fine with that?

@kikofernandez
Copy link
Contributor

@TobiasWrigstad the random function doesn't do any downcast, it's always 32-bits. So I think this is ok.
Merging PR in 30 min

@kikofernandez kikofernandez merged commit 13b916f into development Jun 13, 2016
@kikofernandez kikofernandez deleted the stdlib/random branch June 13, 2016 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants