-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Is Crystal slower than Ruby in generating randoms? #6340
Comments
|
did you compile it with --release? for me 0.25.1 is 10-20% faster than ruby. |
Comparing what's comparable, namely the default random and the secure random source ( # ruby
require "benchmark"
require "securerandom"
N = 1_000_000
RANGE = 0...999_999
Benchmark.bm do |x|
x.report("rand()") { N.times { rand(RANGE) } }
x.report("SecureRandom.rand") { N.times { SecureRandom.rand(RANGE) } }
x.report("SecureRandom.random_bytes") { N.times { SecureRandom.random_bytes(16) } }
end # crystal
require "benchmark"
require "random/secure"
N = 1_000_000
RANGE = 0...999_999
Benchmark.bm do |x|
x.report("rand()") { N.times { rand(RANGE) } }
x.report("Random::Secure.rand") { N.times { Random::Secure.rand(RANGE) } }
x.report("Random::Secure.random_bytes") { N.times { Random::Secure.random_bytes(16) } }
end No comment: $ ruby bench.rb
user system total real
rand() 0.134299 0.000000 0.134299 ( 0.134408)
SecureRandom.rand 1.159312 2.414552 3.573864 ( 3.580610)
SecureRandom.random_bytes 0.782822 3.070608 3.853430 ( 3.860538)
$ crystal run --release bench.cr
user system total real
rand() 0.010000 0.000000 0.010000 ( 0.012934)
Random::Secure.rand 0.130000 1.200000 1.330000 ( 1.337170)
Random::Secure.random_bytes 0.190000 1.990000 2.180000 ( 2.128027) |
@ysbaddaden Thanks for helping me comparing it properly, I appreciate that. I ran your benchs, but used $ ruby bench.rb
user system total real
rand() 1.485554 0.004912 1.490466 ( 1.497553)
SecureRandom.rand 6.764457 0.008698 6.773155 ( 6.783643)
SecureRandom.random_bytes 4.735023 0.004706 4.739729 ( 4.745405)
$ crystal run --release bench.cr
user system total real
rand() 0.050000 0.000000 0.050000 ( 0.053843)
Random::Secure.rand 5.330000 8.670000 14.000000 ( 14.022774)
Random::Secure.random_bytes 5.900000 18.360000 24.260000 ( 24.136340) What could I be doing wrong now? |
@lxxxvi What's your OS? I'm on OSX and my results are:
So on OSX Crystal is definitely slower than Ruby, at least for |
Hmm, is there some sort of precision cutoff going on for the crystal values? The values seem suspiciously round. |
@yxhuvud Indeed! It seems Ruby 2.5.1 uses libc |
@lxxxvi Also, what version of Ruby are you using? It seems 2.5.1 now uses |
I used the very slow N = 10_000_000
bytes = Bytes.new(16)
N.times { Random::Secure.random_bytes(bytes) } $ crystal run --release bench.cr
user system total real
Random::Secure.random_bytes 1.060000 19.240000 20.300000 ( 20.316772) (can't compare with Ruby anymore) |
I use(d) these versions (see the issue's description) $ crystal -v
Crystal 0.25.1 (2018-06-29)
LLVM: 5.0.2
Default target: x86_64-apple-macosx $ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17] Environment
|
It seems Ruby is using https://github.com/ruby/ruby/blob/trunk/random.c#L472-L479 So maybe that's the reason Ruby is faster? /cc @ysbaddaden |
(I wonder how can we detect if a C function is present, when compiling... otherwise I don't know how can we conditionally use |
Doing this in Crystal: lib LibC
fun arc4random_buf(buf : UInt8*, bytes : SizeT)
end
time = Time.now
10_000_000.times do
bytes = Bytes.new(16)
LibC.arc4random_buf(bytes, 16)
end
puts Time.now - time Takes 0.24 seconds. Assuming Ruby uses that (I think so), doing it in Ruby: require "securerandom"
time = Time.now
10_000_000.times do
SecureRandom.random_bytes(16)
end
puts Time.now - time Takes 13.8 seconds. So here's our chance to optimize :-) By the way,
|
@asterite That looks promising 👍 What could be the reason that Looking at your benchmark results, the times for |
@lxxxvi |
@asterite - Adding: {% elsif flag?(:darwin) %}
require "./unix/arc4random" to this and changing a few other things in lib c should cover it. I would also like to see
|
Well, arc4random is only deemed secure on OpenBSD (because ChaCha, and slow). See libsodium sysrandom, which I trust the most. Different systems have different secure sources. Linux 4.x added the Anyway:
Of course if @chris-huxtable we already have uniformisation, for any PRNG. See https://github.com/crystal-lang/crystal/blob/master/src/random.cr |
@ysbaddaden - Syscalls ( |
Sure, but the main issue remains: is arc4random the same as urandom on all supported macOS versions ? That being said, we only ever open a single fd to urandom. |
@ysbaddaden - macOS has had With regards to
I would note that
and
While this doesn't explicitly say it is based on how OpenBSD does it and how I remember reading something to effect a number of years ago (I could be mistaking the source/context), I think its a safe bet. |
I spent too much time already on this issue. I don't place bets on a CSPRNG.
What's sure is that the "kernel automatically seeds the algorithm with additional entropy during normal execution" for the random device (i.e. |
According to source it is reseeded regularly, after each 6.4Mb of random data. It uses |
6.4MiB of random is a LOT of random |
@konovod sadly, the most recent version of the file is dated of 2008 and specifically refers to RC4 😭 |
https://opensource.apple.com |
Is there any John Apple we can ask? |
Apple running hideously old versions of open source code is sadly not uncommon :( |
I may be wrong here but, it also appears that it is reseeded after a fork. in if (rng_state != NULL) {
arc4_count = 0; in if (arc4_count <= 0) {
arc4_stir();
} |
@ysbaddaden - Unfortunately when Apple updated the ADC for Swift a number of years ago they killed off a ton of c and systems documentation. :( |
For Crystal: N = 1_000_000 For Ruby N = 10_000_000 Crystal: Crystal seems to be the winner here.... it cut through the rand() like butter. System: Intel I5, Windows 10, the tests were conducted using the WSL Linux Substem for both Ruby and Crystal. |
@98-f355-f1 yeah thats expected on linux systems, osx benchmark results will be slower |
All I get is rand' called for SecureRandom:Module (NoMethodError) :)
|
Closing. Security trumps performance on this topic. Also, Ruby made changes, and:
Note that libsodium now uses |
for those following this issue if you are interested here are the 2 most active libsodium libraries that include multiple ChaCha algorithms and other cryspographic algorithms. https://github.com/didactic-drunk/sodium.cr |
Just poking around, this change seems to effect about a 2x speedup?
benchs (OS X, git master)
Though I don't understand fully what's going on...doesn't seem to affect startup time, can be synchronized with a mutex and doesn't really drop in speed. FWIW... |
Please see other pull requests about why we don't buffer urandom. |
A comment in the source code might have been nice... |
For followers, here's the PR referred to: #5848 Here's another attempt to speedup, by using OpenSSL's rand function (which I noticed in the ruby source). No speedup in linux, but OS X:
Implementation:
Hard to exactly detect the startup penalty but it seems to be around 0.003s on my local box. FWIW... And here's the long discussion of why ruby switched away from this kind of OpenSSL random default (blog links at top are interesting) https://bugs.ruby-lang.org/issues/9569 for followers... |
No, please. There are years of arguments against OpenSSL's rand function. Ruby finally changed to use proper secure sources, and only kept a fallback on OpenSSL that shall never happen in practice —that we didn't even bother to implement, on purpose. |
Don't bother tweaking Random::Secure. It doesn't have to be fast, it has to be a CSPRNG, meant for cryptography and secure usages such as generating secret keys, or seeding a PRNG. Free performance is great, but it musn't impact the cryptography quality. It's already the best we can achieve today, secure & performance wise —except for FreeBSD 12 where we could leverage
|
Adds documentation based on @ysbaddaden's comment in crystal-lang#6340 (comment)
Hey there. I've just got a question...
Is Crystal slower than Ruby in generating randoms?
While playing around with Crystal I noticed that Ruby is quite faster than Crystal in generating randoms.
I wrote a Crystal script that performs 10 million randoms (
hex
,base64
,rand
andrandom_bytes
) and measures the times.I did the same in Ruby and found that the Ruby script is significantly faster than Crystal.
Is this expected? Are Crystal randoms more secure and thus more expensive?
hex
base64
rand
random_bytes
(In Crystal I used
Random
, in Ruby I usedSecureRandom
)Environment
(This description and all scripts are available at https://github.com/lxxxvi/ruby-vs-crystal-random-performance )
The text was updated successfully, but these errors were encountered: