-
Notifications
You must be signed in to change notification settings - Fork 74
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
Portability issue in hashes #1244
Comments
A couple minor notes:
|
Also:
|
I have a couple of questions related to this:
|
I'm not sure if this would help with local files created by the targets in the pipeline. RDS is the only file format where I would expect headers to be in a place for |
This helps for in-memory R objects only - these are always implicitly serialized before they can be hashed. For files, these are hashed as-is - it's a binary blob. So if the same file is moved to a different machine, it will hash the same. The actual serialization method used to generate the files will determine if the same files are produced on different machines. I hope that makes sense. |
This makes sense, but an area of ambiguity is RDS targets, not generated by |
As far as I'm aware that shouldn't be a problem. You should be able to move RDS files to a different machine with different R version and locale and get identical R objects when loaded (assuming both support R serialization version 3 i.e. R >= 3.5). |
Ah, but while the R objects loaded are identical, I believe targets is hashing the RDS files after writing and before reading. |
If the same object, identical on 2 different computers with different locales are both saved as RDS files and these files are hashed, then the hashes will be different. The R language only guarantees that a round trip serialization and unserialization gives you identical objects regardless of where this is done. Unfortunately it does not guarantee hash stability. |
@wlandau so you're clear on this, the underlying functions called for files and objects are completely separate - files don't go through the R serialization mechanism, so {secretbase} wouldn't 'detect' anything if hashing a file. It might be possible to stream unserialize hash an RDS file (if we know it's an RDS file), but I'm not certain and it'd be quite some effort! |
@wlandau in case you were wondering, the PR shikokuchuo/secretbase#5 was ultimately not merged. After further research, I think it would be worth the while to implement another hash, given we'll be breaking anyway. My original motivation for trying XXH64 was to make it non-breaking if you remember. |
Thanks @shikokuchuo for chiming in here. @noamross, I believe files moved from one machine to another should still have the same hashes. After #1244 is fixed, a pipeline moved to a different machine should stay up to date. If the contents of e.g. |
@wlandau Thanks. I see that moving files and and metadata together from one machine to another should maintain the state of the pipeline. I'm trying to understand (and if possible, expand) the conditions under which different systems can produce byte-identical R objects and be able to compare or share them (for purposes related to the extensions/experiments I discuss in #1232 (comment)). It seems it will probably require loading and re-hashing an object in memory, but I was curious if it is possible on objects serialized to disk. |
@noamross as in my earlier response to @wlandau I think theoretically it would be possible to special case RDS files and pass these to our hash function attached to R unserialize (as opposed to serialize which is what we do for R in-memory objects), and skip the headers in the process. I don't know in practice if there would be any implementation obstacles. But this is a lot of effort and unless this is somehow more broadly useful, I don't think anyone will implement such functionality. |
As in if this is of primary concern, then probably save the files as some other invariant format. |
Indeed, I was just thinking I should test how this all ends up working with |
Reverting back to the issue, shikokuchuo/secretbase#6 is now ready. This implements SipHash (specifically SipHash-1-3 which is a highly-performant variant). This is a higher quality hash than the non-cryptographic hashes such as 'xxhash', and has some security guarantees whilst still being around the same speed. Technically it is not a cryptographic hash, but a pseudorandom function. Whilst we do not need the security guarantees here (and we are using it with the fixed reference key to maximise performance), the collision resistance is guaranteed vs. something like 'xxhash' where trivial collisions have been found and the quality of the hash has been questioned. It has also seen wide adoption e.g. as the default hash for newer Python, in Rust and various other languages. I'll invite you to try it out before merging. Feel free to post comments on the PR itself. |
@wlandau SipHash is now merged into the main branch of |
Awesome! I will benchmark it later this week. |
As we discussed: given its deliberate focus on small data, as well as shikokuchuo/secretbase#8, I am having second thoughts about SipHash for @shikokuchuo, for |
Or, as long as |
I'm still quite jetlagged - I forgot I was meant to be implementing SipHash anyway. From your benchmarking, the total time for file hashing is much lower than for in-memory hashing. I still think SipHash is a good choice. It is a fast hash and I am not aware of another good quality hash that is faster. |
I forgot I implemented this, but Before switching |
From a closer look at https://eprint.iacr.org/2012/351.pdf, I see comments like:
When the authors claim SipHash is "optimized for short inputs", I think they mean it reduces this overhead. It does not necessarily mean the hash is slow for large inputs, which was originally my main concern. But also:
Maybe the authors are comparing SipHash with cryptographic hash functions, maybe not. So I am not sure how much better SipHash-1-3 is than xxhash64 with respect to collision resistance. (Although I would not expect xxhash64 to be better). |
Your guess is as good as mine as to that throw-away comment. But it is designed to guard against generated collisions which are trivial for the weaker hashes https://www.youtube.com/watch?v=Vdrab3sB7MU That's why it's been adopted as the hash used by Python, Rust etc. By definition if it is a true PRF as claimed (indistinguishable from a random uniform generator), then it should not be any worse at collisions than other hashes. |
… the hashing functions from digest::digest() to secretbase::siphash13().
… the hashing functions from digest::digest() to secretbase::siphash13().
… the hashing functions from digest::digest() to secretbase::siphash13().
@shikokuchuo discovered an issue with how
targets
is usingdigest
(either that or a bug indigest
, depending on which behaviors were intended). C.f. shikokuchuo/secretbase#5 (comment).targets
uses serialization version 3 in order to benefit from ALTREP:targets/R/utils_digest.R
Lines 33 to 42 in 0625aa2
targets/R/utils_digest.R
Line 46 in 0625aa2
In
digest
version 0.6.34, the default argumentskip = "auto"
does not appear to skip any of the extra headers introduced by serialization version 3. In other words, these extra serialization headers are hashed together with the contents of the R object. One of these extra headers contains the locale, which is brittle and depends on the user's computing environment. @shikokuchuo confirmed this:The upshot is that hashes in
targets
are currently not portable: if you migrate a project from one machine to another, or if you change the locale of your R session, your pipeline may no longer be up to date.This portability issue needs to be corrected. But unfortunately, a completely back-compatible patch is impossible because:
Switching to serialization version 2 would fix the problem. However, I prefer not to head in that direction because I want to take this opportunity to solve multiple problems at once. Those other problems are:
targets
uses bothsecretbase
anddigest
, which is cumbersome as far as dependencies are concerned because these packages overlap a lot. Andtargets
needssecretbase
for Statistical independence of pseudo-random numbers #1139.secretbase
appears to be faster thandigest
, at least in my own cursory testing, and it does not need the same cumbersome vectorization tricks to achieve low overhead (see benchmarks in xxHash64 implementation (clean) shikokuchuo/secretbase#5).So my current plan is to switch to
secretbase::xxh64()
(c.f. shikokuchuo/secretbase#5) and take time to think about the 32-bit case.The text was updated successfully, but these errors were encountered: