golang 随机数演化

news/2024/10/21 3:05:56/

在Go1.22版本中引入了math/rand/v2包,为原math/rand带来了必要提升

随机数特性

假随机

无论是原版本还是v2版本都不存在真正的随机数,其本质依然是根据初始种子生成的数字序列

全局随机数并发安全

对于原math/rand,全局随机数生成器rngSource在每次获取随机数后,会重新设置rng.vec的值,所以会出现并发冲突。因此通过sync.Mutex来进行并发竞争处理

type lockedSource struct {lk sync.Mutexs  *rngSource
}func (r *lockedSource) Int63() (n int64) {r.lk.Lock()n = r.s.Int63()r.lk.Unlock()return
}

对于v2,全局随机数则是通过在绑定的系统线程获取chacha8随机数生成器,这个锁显然粒度更低,仅在系统线程绑定的协程中加锁

// rand returns a random uint64 from the per-m chacha8 state.
// Do not change signature: used via linkname from other packages.
//go:nosplit
//go:linkname rand
func rand() uint64 {// Note: We avoid acquirem here so that in the fast path// there is just a getg, an inlined c.Next, and a return.// The performance difference on a 16-core AMD is// 3.7ns/call this way versus 4.3ns/call with acquirem (+16%).mp := getg().mc := &mp.chacha8for {// Note: c.Next is marked nosplit,// so we don't need to use mp.locks// on the fast path, which is that the// first attempt succeeds.x, ok := c.Next()if ok {return x}mp.locks++ // hold m even though c.Refill may do stack split checksc.Refill()mp.locks--}
}

原math/rand问题

生成器并不是最优的

可重复性要求意味着无法在不破坏兼容性的情况下替换生成器

Source定义为缩短的63位

Source接口定义为缩短的63位,这并不是现代生成器的uint64

全局生成器的初始化种子责任不明

大多数用户直接使用全局生成器,而全局生成器默认Seed(1),这意味着随机数生成的都是一致的

全局生成器不易拓展

全局生成器为了保护共享的生成器状态,分布到各goroutine会造成锁竞争加剧,此外由于用的同一初始种子,破坏了随机数生成器的可重复性

分裂

在原实现中,先求得小于等于1<<63 - 1且模n为0的最大的int64值,这样确保生成的随机数在取模n后能够均匀分布在[0,n)范围内,但也由此带来了性能问题。

Lemire算法可以使得rand.Intn(1000)快20~30%,可是由于破坏了可重复性,所以无法在原实现直接引入

func (r *Rand) Int63n(n int64) int64 {if n <= 0 {panic("invalid argument to Int63n")}max := int64((1<<63 - 1)  - (1<<63)%uint64(n))v := r.src.Int63()for v > max {v = r.Int63()}return v % n
}

误用Read生成密钥

math/rand并不用于也不适合生成加密密钥

math/rand/v2的解决方案

为解决以上问题,在v2中引入了PCG和chacha8两种随机数生成器实现,更改source接口为现代的uint64。

在全局生成器上,保证每次调用都是全新的种子。此外由于默认全局生成器实现就是chacha8,那么即使误用生成密钥,也不会有特别大的问题

Ref

  1. https://go.dev/blog/randv2

http://www.ppmy.cn/news/1457865.html

相关文章

springboot集成-Redis

Spring Boot项目中集成Redis&#xff0c;可以使用Spring Data Redis来简化操作。首先需要在pom.xml文件中添加Redis和Spring Data Redis的依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter…

【编码利器 —— BaiduComate】

目录 1. 智能编码助手介绍 2. 场景需求 3. 功能体验 3.1指令功能 3.2插件用法 3.3知识用法 3.4自定义配置 4. 试用感受 5. AI编程应用 6.总结 智能编码助手是当下人工智能技术在编程领域的一项重要应用。Baidu Comate智能编码助手作为一款具有强大功能和智能特性的工…

AI大模型探索之路-训练篇17:大语言模型预训练-微调技术之QLoRA

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

深度学习实例1_mnist识别手写数字——自学笔记

import torch import numpy as np from matplotlib import pyplot as plt from torch.utils.data import DataLoader from torchvision import transforms from torchvision import datasets import torch.nn.functional as F导入mnist数据集,包括训练集图片、训练集标签、测试…

一起刷C语言菜鸟教程100题(15-26含解析)

五一过的好快&#xff0c;五天假期说没就没&#xff0c;因为一些事情耽搁到现在&#xff0c;不过还是要继续学习的&#xff0c;之后就照常更新&#xff0c;先说一下&#xff0c;这个100题是菜鸟教程里面的&#xff0c;但是有一些题&#xff0c;我加入了自己的理解&#xff0c;甚…

11.偏向锁原理及其实战

文章目录 偏向锁原理及其实战1.偏向锁原理2.偏向锁案例代码演示2.1.偏向锁案例代码2.2.1.无锁情况下状态2.1.2.偏向锁状态2.1.3.释放锁后的状态 2.2.偏向锁的膨胀和撤销2.2.1.偏向锁撤销的条件2.2.2.偏向锁的撤销 2.2.3.偏向锁的膨胀 2.3.全局安全点原理和偏向锁撤销性能问题2.…

C语言——文件描述符、系统调用操作文件

文件描述符 在Unix-like操作系统中&#xff0c;文件描述符&#xff08;file descriptor&#xff09;是一个用于标识打开文件或I/O设备的整数值。它是对底层文件系统的抽象&#xff0c;用于在应用程序和操作系统之间传递文件信息。 文件描述符是一个非负整数&#xff0c;通常是…

【FX110】2024外汇市场中交易量最大的货币对是哪个?

作为最大、最流动的金融市场之一&#xff0c;外汇市场每天的交易量高达几万亿美元&#xff0c;涉及到数百种货币。不同货币对的交易活跃程度并不一样&#xff0c;交易者需要根据货币对各自的特点去进行交易。 全年外汇市场中涉及美元的外汇交易超过50%&#xff01; 实际上&…