首页 > 其他分享 >random随机数性能问题

random随机数性能问题

时间:2022-12-13 16:35:00浏览次数:39  
标签:rte __ rand 性能 random uint64 state 随机数


目录

  • ​​random函数性能问题​​
  • ​​random函数实现​​
  • ​​lrand48实现​​
  • ​​lrand48_r实现​​
  • ​​dpdk中的rte_rand​​
  • ​​dpdk 18.11中的 rte_rand​​
  • ​​dpdk 20.11中的 rte_rand​​
  • ​​自定义比较好的随机函数​​
  • ​​参考​​

random函数性能问题

测试dpvs新建conn的性能过程中,根据火焰图,发现random函数占用了大量的cpu资源,如下所示。

random随机数性能问题_随机数

random函数实现

man random 知道,random函数为glibc的库函数,查看源码如下所示:

long int
__random (void)
{
int32_t retval;
__libc_lock_lock (lock);
(void) __random_r (&unsafe_state, &retval);
__libc_lock_unlock (lock);
return retval;
}

weak_alias (__random, random)
ps:
glibc源码在线查看:https://elixir.bootlin.com/glibc/latest/source/stdlib/random.c#L300

分析:
如上所示,random添加了锁,所以是线程安全的函数,但是锁影响了性能。

lrand48实现

lrand48同样是库函数,实现如下所示:

long int
lrand48 (void)
{
long int result;
(void) __nrand48_r (__libc_drand48_data.__x, &__libc_drand48_data, &result);
return result;
}

分析:
如上所示,lrand48

  • 没有加锁
  • 不是线程安全的,多线程下会有cache miss的开销问题
    因为多个线程公用了 __libc_drand48_data 变量。在 __nrand48_r—> __drand48_iterate 中 会对其进行更改。
    一个线程对于共享变量进行更改,则另外一个线程读取就需要从内存中读取,而不可以从cache中读取,因为cache从内存中一次性读取一个cacheline的数据,一个线程对于数据进行更改,那么其他线程中cache中保存的该数据就不是最新的数据了。
    所以:不是线程安全(函数中读以及写了static变量或者全局变量)的函数;多线程之间,就可能存在cache 一致性同步的问题

lrand48_r实现

int lrand48_r (struct drand48_data *buffer, long int *result)
{
/* Be generous for the arguments, detect some errors. */
if (buffer == NULL)
return -1;
return __nrand48_r (buffer->__x, buffer, result);
}

分析:

  • 没有加锁
  • 自定义生成随机数的种子,可以做到线程安全;
    如上所示,lrand48_r 自定义生成随机数的种子数据,将生成的随机数保存到 result中。
    每个线程都有自己的随机数种子,这样可以保证 lrand48_r是线程安全的,且可以做到不存在cache miss。

dpdk中的rte_rand

dpdk 18.11中的 rte_rand

static inline uint64_t rte_rand(void)
{
uint64_t val;
val = (uint64_t)lrand48();
val <<= 32;
val += (uint64_t)lrand48();
return val;
}

分析
通过调用两次的 lrand48 生成一个 uint64的随机数。
但是 lrand48 由于共享变量,在多线程之间存在 cache 一致性同步的问题。

dpdk 20.11中的 rte_rand

struct rte_rand_state {
uint64_t z1;
uint64_t z2;
uint64_t z3;
uint64_t z4;
uint64_t z5;
} __rte_cache_aligned;
static struct rte_rand_state rand_states[RTE_MAX_LCORE];

static __rte_always_inline
struct rte_rand_state *__rte_rand_get_state(void)
{
unsigned int lcore_id;
lcore_id = rte_lcore_id();
if (unlikely(lcore_id == LCORE_ID_ANY))
lcore_id = rte_get_main_lcore();
return &rand_states[lcore_id];
}

uint64_t rte_rand(void)
{
struct rte_rand_state *state;
state = __rte_rand_get_state();
return __rte_rand_lfsr258(state);
}

static __rte_always_inline uint64_t
__rte_rand_lfsr258(struct rte_rand_state *state)
{
state->z1 = __rte_rand_lfsr258_comp(state->z1, 1UL, 53UL,
18446744073709551614UL, 10UL);
state->z2 = __rte_rand_lfsr258_comp(state->z2, 24UL, 50UL,
18446744073709551104UL, 5UL);
state->z3 = __rte_rand_lfsr258_comp(state->z3, 3UL, 23UL,
18446744073709547520UL, 29UL);
state->z4 = __rte_rand_lfsr258_comp(state->z4, 5UL, 24UL,
18446744073709420544UL, 23UL);
state->z5 = __rte_rand_lfsr258_comp(state->z5, 3UL, 33UL,
18446744073701163008UL, 8UL);
return state->z1 ^ state->z2 ^ state->z3 ^ state->z4 ^ state->z5;
}

分析:

  • rand_states[RTE_MAX_LCORE]数组,每个core有一个自己独占的元素;
  • struct rte_rand_state 是cacheline对齐的,即每个core对应的元素是cacheline对齐的,各个元素之间的更改互不影响。

这个很重要,因为数组中每个元素如果不是cacheline对齐的,那么整个数组就会是在一个cacheline中。
一个线程对于自己对应的数组元素进行更改,则会造成另外一个线程,其cache中数据为老数据,需要从内存中读取。因为cpu每次从内存中读取一个cacheline的数据,同时在cache中保存一份备份。
一个线程对于自身对应元素的更改,虽然不会更改其他元素的值,会影响到整个cacheline的变化。那么其他的每个core对应的cache中的数据就都是老数据了。

自定义比较好的随机函数

dpdk 中  获取时间戳的方法:
Intel的X86中的RDTSC即Read Time Stamp Counter 读取时间计数器的指令。

static inline uint64_t rte_rdtsc(void)
{
union {
uint64_t tsc_64;
RTE_STD_C11
struct {
uint32_t lo_32;
uint32_t hi_32;
};
} tsc;

...

asm volatile("rdtsc" :
"=a" (tsc.lo_32),
"=d" (tsc.hi_32));
return tsc.tsc_64;
}

分析:

  • 背景:

Linux提供的API——gettimeofday()可以获取微秒级的精度。但是,首先它不能提供纳秒级精度,其次,他是一个系统调用,自身就有一定的开销。

  • Tsc介绍

每个核都有自己独立的一套寄存器,TSC是一个64位的寄存器
从Intel Pentium开始,在所有的x86平台上均会提供。
TSC寄存器中存放的是CPU从启动以来执行的指令周期数
通过rdtsc指令,可以将TSC的数值存放在EDX:EAX中。如下所示:

uint64_t get_tsc() { uint64_t a, d; __asm__ volatile("rdtsc" : "=a"(a), "=d"(d)); return (d << 32) | a; }TSC曾经是一个==极高精度,极低开销==的取时间的方法。

TSC的坑:

结论:

  • TSC寄存器在每个core中都有一份,其值是放在寄存器中。
    所以获取TSC(cpu启动以来的指令周期数)的值的开销极低
  • 可基于rdtsc,然后乘以一个放大的系数,再mode一个数字,得到一个随机数。

参考

dpdk中线程安全的高性能伪随机数生成器:
https://www.dpdk.org/wp-content/uploads/sites/35/2019/10/Threadsafe.pdf

TSC的坑
http://www.wangkaixuan.tech/?p=901


标签:rte,__,rand,性能,random,uint64,state,随机数
From: https://blog.51cto.com/u_15911260/5934627

相关文章

  • 拷贝函数strncpy/snprintf/memcpy性能分析
    目录​​strncpy​​​​snprintf实现拷贝​​​​memcpy​​​​dpdk的rte_memcpy​​​​参考​​strncpysnprintf实现拷贝memcpydpdk的rte_memcpy参考strncpy和snp......
  • 比较函数strcmp/memcmp性能分析
    目录​​strcmp​​​​实现​​​​性能分析​​​​memcmp​​​​实现​​​​性能分析​​​​参考​​strcmp实现性能分析memcmp实现性能分析参考dpdk中对于rte_memc......
  • 置0函数memset/bzero性能分析
    目录​​memset​​​​实现​​​​bzero​​​​实现​​​​memset和bzero对比​​​​参考​​memset实现bzero实现memset和bzero对比参考https://www.jb51.net/art......
  • 使用 CNG API 生成伪随机数
    //////File:RandomNumGeneration.cpp////Contents:ThissampleshowsrandomnumbergenerationinCNG.////#defineWIN32_NO_STATUS#in......
  • 性能达1.5+倍!昇腾AI助力分子动力学模拟研究
    摘要:在异构计算架构CANN的助力下,AI预测性能达到现有产品的1.5+倍,可预测规模较传统方法提升10000+倍,为光伏材料、新能源电池、半导体材料研究带来巨大的商业应用价值。本文......
  • 集合性能优化相关
    【Collections.singletonList】被限定只被分配一个内存空间,也就是只能存放一个元素的内容。这样做的好处就是不会造成内存的浪费,不像ArrayList这样的类,不管你是需要多少内......
  • 【Unity】 HTFramework框架(四十)Debug的性能监控
    更新日期:2021年4月22日。Github源码:​​​[点我获取源码]​​​Gitee源码:​​[点我获取源码]​​索引​​C#代码性能监控​​​​使用​​​​Debug的性能监控模式​​​......
  • STM32H747IIT6(2MB)STM32H747IGT6(1MB)STM32H747BIT6高性能MCU资料
    概述:STM32H7高性能MCU基于高性能ArmCortex-M732位RISC内核,工作频率高达400MHz。Cortex-M7内核具有浮点单元(FPU)精度,支持Arm双精度(符合IEEE754标准)和单精度数据处理指......
  • 记一次 .NET 程序的性能优化实战(4)—— .NET 源码查看及使用 vs 调试
    记一次.NET程序的性能优化实战(4)——.NET源码查看及使用vs调试原总结工具调试dotNETpart4regex正则表达式vsdnspy前言在前面三篇文章中应该已经把​​.NET​​中正......
  • 【JVM】VisualVM Java程序性能分析工具
     【JVM】VisualVMJava程序性能分析工具 介绍VisualVM是一个集成多个JDK命令行工具的可视化工具。可以作为Java应用程序性能分析和运行监控的工具。开发人员可以利用它来......