A story of two random number functions

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed. I often have some confusion about how ' crypto/rand ' Packages and ' Math/rand ' packages are related, or how they work as expected (together). Is this a question that other people have thought about, or is it just my own whim? Finally one day, I decided to conquer the problem, this blog is the result of this effort. # # ' Math ' package if you've ever followed the ' Math/rand ' package, you'll agree that it provides a fairly easy-to-use API. My favorite example is the ' func Intn (n int) int ' function, which returns a random number within the range you specify. Very useful! You might ask what is the difference between a top-level function and an instance function of the ' Rand ' type. If you look at [source code implementation] (HTTPS://GOLANG.ORG/SRC/MATH/RAND/RAND.GO), you'll find that the top-level function is just an easy-to-use package that points internally to a package global object ' Globalrand '. Although this package has some small pitfalls to use. The most basic usage is to provide a * * pseudo-random * * Number as a seed. This means that if you use the same seed to generate two ' Rand ' instances, and the same sequence and function calls are made to these two instances, you will get two strings of * exactly the same * output. (I found that this overturned my perception of the concept of "random numbers" because I didn't want to be able to predict "random" results.) If two ' Rand ' objects use different values to seed, they do not have the same behavior. # # ' Crypto ' package now, let's take a look at the ' Crypto/rand ' package. This is a sophisticated and precise API interface. My understanding is that it generates completely different random sequences based on the random number generator at the bottom of the operating system. The only question is: how do I use it??? I was able to get a random byte slice of 0 and 1, but how to deal with it? It's not as easy to use as the ' Math/rand ' package, is it? Well, is it possible to get the real randomness of the ' Crypto/rand ' package and get the ease of ' math/rand ' package? Perhaps the real question is: how do you combine these two disparate packages? # # One plus one greater than two (note: reference video [VINTAGE ' S reeses peanut BUTTER CUPS commercial W WALKERS] (Https://www.youtube.com/watch?v=DJLDF6qZ UX0)) Let's dive into the ' Math/rand ' package. We pass an ' RAnd. Source ' to instantiate ' Rand. Rand ' type. But like most Go idioms, this ' Source ' is an interface. My sixth sense is coming, maybe this is a chance? ' Rand. Source ' main work is done by the ' Int63 () Int64 ' function, which returns a non-negative ' int64 ' integer (that is, the highest bit is 0). Further improvements to ' Rand. Source64 ' simply returns a ' UInt64 ' type, and does not have any restrictions on the highest bit. You say we try to create a ' rand ' using the functionality derived from the ' Crypto/rand ' package. What about Source64 ' objects? (You can refer to the code on [Go Playground] (Https://play.golang.org/p/_3w6vWTwwE). First of all, we are for our ' rand. Source64 ' creates a structure. (Also note: Because the ' math/rand ' and ' crypto/rand ' will conflict when used, in the code below, we will use ' mrand ' and ' cand ' instead. "' Gotype mysrc struct{}" Let's declare the ' Seed (...) for the interface. function We don't need a seed that interacts with the ' Crypto/rand ' package, so there's no specific code. ' Gofunc (S *mysrc) seed (seed int64) {/*no-op*/} ' because the ' Uint64 () ' function returns the value range * (widest) * *, requires a 64-bit random number, so we first implement it. We use the ' encoding/binary ' package from ' Crypto/rand ' to the ' io ' package. Reader ' interface to read 8 bytes of data and switch directly to ' UInt64 '. "' Gofunc (S *mysrc) Uint64 () (value UInt64) {binary. Read (Crand. Reader, Binary. Bigendian, &value) return value} ' Int63 () ' function is similar to ' Uint64 () ' function, we just have to ensure that the highest bit is 0. This is fairly simple, just to do a quick bit mask operation on the basis of the ' Uint64 () ' return value. "' Gofunc (S *mysrc) Int63 () Int64 {return INt64 (S.uint64 () & ^uint64 (1<<63))} "very good!" Now we have the full version of ' Rand '. Source64 ' was fulfilled. Let's do some testing to verify. "' Govar src mrand. SOURCE64SRC = &mysrc{}r: = Mrand. New (SRC) fmt. Printf ("%d\n", R.intn (23)) "# # Trade-offs cool, with just more than 10 lines of code above, we have a very simple solution that combines password-safe random number generation and ' Math/rand ' package-friendly APIs organically. However, I gradually realized that nothing was free. What is the cost of using this scheme? Let's perform [performance analysis] (https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go) for this piece of code. (Note: I like to use prime numbers in tests, so you'll see a lot of 7919 as parameters, it's the 1000th prime number.) What is the performance of the top-level function in the ' Math/rand ' package? "' Gofunc Benchmarkglobal (b *testing. B) {for n: = 0; n < b.n; n++ {result = Rand. INTN (7919)}} "Good!" It's about a ns/op on my laptop. "' BenchmarkGlobal-4 50000000 37.7 ns/op ' If you create an ' rand ' with the current time as the seed. Rand ' instance, what would happen? "' Gofunc benchmarknative (b *testing. B) {random: = rand. New (Rand. Newsource (time. Now (). Unixnano ()))) for n: = 0; n < B.N; n++ {result = random. INTN (7919)}} "about Ns/op, pretty good!" "' BenchmarkNative-4 100000000 22.7 ns/op ' Now, let's test our new seed scheme. "' Gofunc Benchmarkcrypto (b *testing. B) {random: = rand. New (&mysrc{}) for n: = 0; N <B.N; n++ {result = random. INTN (7919)} ' Oops, about ns/op, it's too expensive. Is there somewhere we got it wrong? Or is that what it takes to pay for the ' Crypto/rand ' package? "BenchmarkCrypto-4 2000000 867 ns/op" Let's test how long it takes to read ' Crypto/rand ' separately. "' Gofunc Benchmarkcryptoread (b *testing. B) {buffer: = make ([]byte, 8) for n: = 0; n < b.n; n++ {result, _ = Crand. Read (buffer)}} "Good, the results show that most of our new solution is spent on interaction with the ' Crypto/rand ' package. "' BenchmarkCryptoRead-4 2000000 735 ns/op ' I don't know what to do to further improve performance. And, perhaps for your usage scenario, it's not a problem to spend about 1 milliseconds to get a non-specific random number. This needs to be evaluated by yourself. # # Another way of thinking? One of the most familiar uses of randomization is the [exponential Backoff] (https://en.wikipedia.org/wiki/Exponential_backoff) tool. This is done to reduce the chance of accidental synchronization when reconnecting to a pressurized server, because a regular load can cause damage to the server's recovery. In these scenarios, the deterministic random behavior is not an issue in itself, but there is a problem with using the same seed in a group of instances. Also, using the top-level ' math/rand ' function, either using the default seed (i.e., the seed with the implied 1), or using a very easy to observe ' time '. Now (). Unitnano () ' Paradigm to do seed, which would be a problem. If your service happens to start at the same time, the service is forced to abort the exit if the random output is determined to cause an unexpected synchronization. What if we use the power of ' crypto/rand ' to generate the seed of the ' math/rand ' tool when we instantiate it, and then we can still enjoy the performance of deterministic random tools? "' Gofunc newcryptoseededsource () Mrand. Source {var seed int64binary. Read (Crand. Reader, Binary. BigendiAn, &seed) return Mrand. Newsource (Seed)} "we can re-perform profiling of new code, but we already knew that performance would go back to deterministic randomness." "' Gofunc Benchmarksead (b *testing. B) {random: = Mrand. New (Newcryptoseededsource ()) for n: = 0; n < B.N; n++ {result = random. INTN (7919)}} "Now we have confirmed our hypothesis to be correct." "' BenchmarkSeed-4 50000000 23.9 ns/op ' # # about the author hi, I'm Ners Cappentiel. I am a veteran software engineer in San Francisco [Orion Lab] (https://www.orionlabs.io/). I've written the go code for three years, and when I get to know it quickly, go has become one of my favorite languages. Disclaimer: I am neither a security expert nor a cross-platform ' crypto/rand ' implementation expert. If you want to use these tools in your critical security task use case, you can consult with a local security expert. You can get a refined version of the code example from [here] (Https://github.com/orion-labs/go-crypto-source). It follows the Apache 2.0 license, so you can cut and draw any code you need!

via:https://blog.gopheracademy.com/advent-2017/a-tale-of-two-rands/

Author: Nelz Carpentier Translator: Arthurlee proofreading: polaris1119

This article by GCTT original compilation, go language Chinese network honor launches

This article was originally translated by GCTT and the Go Language Chinese network. Also want to join the ranks of translators, for open source to do some of their own contribution? Welcome to join Gctt!
Translation work and translations are published only for the purpose of learning and communication, translation work in accordance with the provisions of the CC-BY-NC-SA agreement, if our work has violated your interests, please contact us promptly.
Welcome to the CC-BY-NC-SA agreement, please mark and keep the original/translation link and author/translator information in the text.
The article only represents the author's knowledge and views, if there are different points of view, please line up downstairs to spit groove

746 reads ∙1 likes

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.