-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
initRand now uses strict monotonic counter to guarantee uniqueness #18149
Conversation
26470a9
to
2669a47
Compare
2669a47
to
dbf6abc
Compare
Just curious: why use the current time, rather than pulling from /dev/random or /dev/urandom (*nix), or CryptGenRandom (Windows)? |
well we now have std/sysrand instead of having to call those directly, so it's an option, but IIRC performance can be a concern, definitely worth trying though; also, we need a vmops for whichever option is used in the end so that |
Well then, I propose using the system's source of randomness as a seed. If someone needs, for some odd reason, to initialize thousands of state machines using a faster method, they can do so manually. Using a monotonic clock, even a strict one, doesn't necessarily guarantee unique values for each call. A random source of data may return the same value consecutively, but that can't be known ahead of time (assuming the system implementation is sound). |
1ec8222
to
d3d1d96
Compare
PTAL, addressed comment; this PR is an improvement over status quo.
the benchmark below shows that using import std/sysrand
import std/monotimes
import timn/exp/cputicks # cf upcoming PR
template algo1(buf, c) =
let ok = urandom(buf)
doAssert ok
c += cast[int](buf[0].addr)
template algo2(buf, c) =
let t = getCpuTicks()
c += cast[int](t)
template algo3(buf, c) =
let t = getMonoTime()
c += cast[int](t.ticks)
template mainAux(algo)=
let n = 10000
var buf: array[8, byte]
var c = 0
let t1 = getCpuTicks()
for i in 0..<n:
algo(buf, c)
let t2 = getCpuTicks()
echo (astToStr(algo), c, t2 - t1)
proc main()=
for i in 0..<10:
echo()
mainAux(algo1)
mainAux(algo2)
mainAux(algo3)
main() |
So this seems to be an argument of performance vs correctness. Either we use a source of cryptographic randomness as the seed, or a high-resolution monotonic timer. Is this accurate? |
yes, but i have a PR in the work that will give both performance and correctness; until then this PR is an improvement over status quo |
If there's a PR in the works for a proper fix, there's little point in a PR for an improper one, unless there's something time-critical going on. |
with this logic, nothing ever gets done (it's not the 1st time). This PR is good enough to close #17898 given the clock resolution and cost of initRand, and improves several other things if you read the PR content. The PR I have in the work could potentially be controversial, who knows (and the other things in this PR are still useful regardless); there's no point in blocking on it when reusing existing |
d3d1d96
to
692b452
Compare
PTAL |
This does not fix #17898, it just makes it less likely. Multiple decisions need to be made here:
In my opinion, it's up to the caller of |
I would also love to know why randomPathName is initializing a new random state each time. If you're going to do that, you might as well just use the current time anyway. |
I agree completely, this is not good. |
3 possible venues:
|
This PR may not fix #17898 completely, but it could be a improvement for random module. |
692b452
to
b2da97b
Compare
@Araq PTAL:
which also addresses the previously raised concerns example
future work
links |
4fa63ea
to
fa6bf6c
Compare
else: | ||
let now = times.getTime() | ||
result = initRand(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond) | ||
result = initRand(getCpuTicks()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this constrains the random module to work only on platforms where high-resolution tick counters are available - given that cputicks depends on non-standard behavior, it severly limits the platforms where Nim can be used - how to init rand
is not a performance-critical operation - in fact, it would be trivial to continue using standardised C API for this without any significant loss - random
is not a cryptographic random source, it's a best-effort proposition upon which no code that actually requires randomness should rely upon - the granularity doesn't not change the utility of the module for any use cases for which its use is appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it doesn't; fallback code can always be added to getCpuTicks
to return something similar to what std/monotimes returns. rdtsc is available on all x86 processors since the pentium, and other platforms that nim supports have equivalent instructions which can be wrapped by getCpuTicks
, see google/benchmark code here https://github.com/google/benchmark/blob/v1.1.0/src/cycleclock.h#L116 which handles more platform than nim supports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and other platforms that nim supports have equivalent instructions which can be wrapped by getCpuTicks
Do you mean it will be implemented in the future? Then change the random.nim
after they are implement I think. Anyway let's wait for #18743.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see also nim-lang/RFCs#414 which would allow testing from presence of __rdtsc
programmatically, at CT
This pull request has been automatically marked as stale because it has not had recent activity. If you think it is still a valid PR, please rebase it on the latest devel; otherwise it will be closed. Thank you for your contributions. |
It was done differently by #18744 |
note
the CI failure seems to suggest(EDIT: that's expected; getCpuTicks would be strict monotonic at least on modern cpus)getMonoTime
is in fact not monotonic on some OS? that's worrysomefuture work
initRand()
on each call torandomPathName
but should instead use athreadvar
rand state, maybe (code doesn't look re-entrant)getThreadId()
to guarantee uniqueness (EDIT: even within same thread there is no such guarantee because it's not strict monotonic; but getCpuTicks would be strict monotonic at least on modern cpus)initRand()
work with--experimental:vmopsDanger
proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".}
in std/monotimes has wrong signature, differing from one in system/timers (see https://developer.apple.com/documentation/kernel/1462446-mach_absolute_time)