imtoken网站链接|hll

作者: imtoken网站链接
2024-03-07 19:08:45

HyperLogLog 算法详解 - 知乎

HyperLogLog 算法详解 - 知乎首发于技术猫开源俱乐部切换模式写文章登录/注册HyperLogLog 算法详解Abser Ari一个好家伙更好的阅读体验在 基数计数基本概念概率算法实际上目前还没有发现更好的在大数据场景中准确计算基数的高效算法,因此在不追求绝对准确的情况下,使用概率算法算是一个不错的解决方案。概率算法不直接存储数据集合本身,通过一定的概率统计方法预估基数值,这种方法可以大大节省内存,同时保证误差控制在一定范围内。目前用于基数计数的概率算法包括:Linear Counting(LC):早期的基数估计算法,LC在空间复杂度方面并不算优秀,实际上LC的空间复杂度与简单bitmap方法是一样的(但是有个常数项级别的降低),都是O(Nmax);LogLog Counting(LLC):LogLog Counting相比于LC更加节省内存,空间复杂度只有O(log2(log2(Nmax)))HyperLogLog Counting(HLL):HyperLogLog Counting是基于LLC的优化和改进,在同样空间复杂度情况下,能够比LLC的基数估计误差更小。HLL直观演示HLLDEMOHLL的实际步骤HLL是LLC的误差改进,实际是基于LLC。算法来源(N次伯努利过程)下面非正式的从直观角度描述LLC算法的思想来源。设a为待估集合(哈希后)中的一个元素,由上面对H的定义可知,a可以看做一个长度固定的比特串(也就是a的二进制表示),设H哈希后的结果长度为L比特,我们将这L个比特位从左到右分别编号为1、2、…、L:又因为a是从服从均与分布的样本空间中随机抽取的一个样本,因此a每个比特位服从如下分布且相互独立。通俗说就是a的每个比特位为0和1的概率各为0.5,且相互之间是独立的。解释注意如下事实:由于比特串每个比特都独立且服从0-1分布,因此从左到右扫描上述某个比特串寻找第一个“1”的过程从统计学角度看是一个伯努利过程,例如,可以等价看作不断投掷一个硬币(每次投掷正反面概率皆为0.5),直到得到一个正面的过程。在一次这样的过程中,投掷一次就得到正面的概率为1/2,投掷两次得到正面的概率是 1/2^k 回到基数统计的问题,我们需要统计一组数据中不重复元素的个数,集合中每个元素的经过hash函数后可以表示成0和1构成的二进制数串,一个二进制串可以类比为一次抛硬币实验,1是抛到正面,0是反面。二进制串中从低位开始第一个1出现的位置可以理解为抛硬币试验中第一次出现正面的抛掷次数k,那么基于上面的结论,我们可以通过多次抛硬币实验的最大抛到正面的次数来预估总共进行了多少次实验,同样可以可以通过第一个1出现位置的最大值 k^{max} 来预估总共有多少个不同的数字(整体基数)。LogLogCounting均匀随机化与LC一样,在使用LLC之前需要选取一个哈希函数H应用于所有元素,然后对哈希值进行基数估计。H必须满足如下条件(定性的):1、H的结果具有很好的均匀性,也就是说无论原始集合元素的值分布如何,其哈希结果的值几乎服从均匀分布(完全服从均匀分布是不可能的,D. Knuth已经证明不可能通过一个哈希函数将一组不服从均匀分布的数据映射为绝对均匀分布,但是很多哈希函数可以生成几乎服从均匀分布的结果,这里我们忽略这种理论上的差异,认为哈希结果就是服从均匀分布)。2、H的碰撞几乎可以忽略不计。也就是说我们认为对于不同的原始值,其哈希结果相同的概率非常小以至于可以忽略不计。3、H的哈希结果是固定长度的。以上对哈希函数的要求是随机化和后续概率分析的基础。后面的分析均认为是针对哈希后的均匀分布数据进行。分桶平均上述分析给出了LLC的基本思想,不过如果直接使用上面的单一估计量进行基数估计会由于偶然性而存在较大误差。因此,LLC采用了分桶平均的思想来消减误差。具体来说,就是将哈希空间平均分成m份,每份称之为一个桶(bucket)。对于每一个元素,其哈希值的前k比特作为桶编号,其中 2^{k} = m ,而后L-k个比特作为真正用于基数估计的比特串。桶编号相同的元素被分配到同一个桶,在进行基数估计时,首先计算每个桶内元素最大的第一个“1”的位置,设为M[i],然后对这m个值取平均后再进行估计,即:这相当于物理试验中经常使用的多次试验取平均的做法,可以有效消减因偶然性带来的误差。下面举一个例子说明分桶平均怎么做。假设H的哈希长度为16bit,分桶数m定为32。设一个元素哈希值的比特串为“0001001010001010”,由于m为32,因此前5个bit为桶编号,所以这个元素应该归入“00010”即2号桶(桶编号从0开始,最大编号为m-1)而剩下部分是“01010001010”且显然ρ(01010001010)=2,所以桶编号为“00010”的元素最大的ρ即为M[2]的值。偏差修正上述经过分桶平均后的估计量看似已经很不错了,不过通过数学分析可以知道这并不是基数n的无偏估计。因此需要修正成无偏估计。这部分的具体数学分析在“Loglog Counting of Large Cardinalities”中,过程过于艰涩这里不再具体详述,有兴趣的朋友可以参考原论文。这里只简要提一下分析框架:首先上文已经得出:因此:这是一个未知通项公式的递推数列,研究这种问题的常用方法是使用生成函数(generating function)。通过运用指数生成函数和poissonization得到上述估计量的Poisson期望和方差为:最后通过depoissonization得到一个渐进无偏估计量:其中:其中m是分桶数。这就是LLC最终使用的估计量。误差分析不加证明给出如下结论:算法应用误差控制在应用LLC时,主要需要考虑的是分桶数m,而这个m主要取决于误差。根据上面的误差分析,如果要将误差控制在ϵ之内,则:内存使用分析合并与LC不同,LLC的合并是以桶为单位而不是bit为单位,由于LLC只需记录桶的,因此合并时取相同桶编号数值最大者为合并后此桶的数值即可。HyperLogLog CountingHyperLogLog Counting(以下简称HLLC)的基本思想也是在LLC的基础上做改进,具体细节请参考“HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm”这篇论文。基本算法HLLC的第一个改进是使用调和平均数替代几何平均数。注意LLC是对各个桶取算数平均数,而算数平均数最终被应用到2的指数上,所以总体来看LLC取得是几何平均数。由于几何平均数对于离群值(例如这里的0)特别敏感,因此当存在离群值时,LLC的偏差就会很大,这也从另一个角度解释了为什么n不太大时LLC的效果不太好。这是因为n较小时,可能存在较多空桶,而这些特殊的离群值强烈干扰了几何平均数的稳定性。因此,HLLC使用调和平均数来代替几何平均数,调和平均数的定义如下:调和平均数可以有效抵抗离群值的扰动。使用调和平均数代替几何平均数后,估计公式变为如下:其中:偏差分析根据论文中的分析结论,与LLC一样HLLC是渐近无偏估计,且其渐近标准差为:因此在存储空间相同的情况下,HLLC比LLC具有更高的精度。例如,对于分桶数m为2^13(8k字节)时,LLC的标准误差为1.4%,而HLLC为1.1%。分段偏差修正在HLLC的论文中,作者在实现建议部分还给出了在n相对于m较小或较大时的偏差修正方案。具体来说,设E为估计值:当时,使用LC进行估计。当是,使用上面给出的HLLC公式进行估计。当时,估计公式如为关于分段偏差修正效果分析也可以在原论文中找到。结论并行化这些基数估计算法的一个好处就是非常容易并行化。对于相同分桶数和相同哈希函数的情况,多台机器节点可以独立并行的执行这个算法;最后只要将各个节点计算的同一个桶的最大值做一个简单的合并就可以得到这个桶最终的值。而且这种并行计算的结果和单机计算结果是完全一致的,所需的额外消耗仅仅是小于1k的字节在不同节点间的传输。应用场景基数估计算法使用很少的资源给出数据集基数的一个良好估计,一般只要使用少于1k的空间存储状态。这个方法和数据本身的特征无关,而且可以高效的进行分布式并行计算。估计结果可以用于很多方面,例如流量监控(多少不同IP访问过一个服务器)以及数据库查询优化(例如我们是否需要排序和合并,或者是否需要构建哈希表)。参考阅读编辑于 2023-06-02 16:15・IP 属地四川算法编程Redis​赞同 77​​12 条评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录技术猫开源俱乐部欢迎所有想学习编程

Reids(4)——神奇的HyperLoglog解决统计问题 - 知乎

Reids(4)——神奇的HyperLoglog解决统计问题 - 知乎首发于【More Than Java】学习之路切换模式写文章登录/注册Reids(4)——神奇的HyperLoglog解决统计问题我没有三颗心脏一、HyperLogLog 简介HyperLogLog 是最早由 Flajolet 及其同事在 2007 年提出的一种 估算基数的近似最优算法。但跟原版论文不同的是,好像很多书包括 Redis 作者都把它称为一种 新的数据结构(new datastruct) (算法实现确实需要一种特定的数据结构来实现)。关于基数统计基数统计(Cardinality Counting) 通常是用来统计一个集合中不重复的元素个数。思考这样的一个场景: 如果你负责开发维护一个大型的网站,有一天老板找产品经理要网站上每个网页的 UV(独立访客,每个用户每天只记录一次),然后让你来开发这个统计模块,你会如何实现?如果统计 PV(浏览量,用户没点一次记录一次),那非常好办,给每个页面配置一个独立的 Redis 计数器就可以了,把这个计数器的 key 后缀加上当天的日期。这样每来一个请求,就执行 INCRBY 指令一次,最终就可以统计出所有的 PV 数据了。但是 UV 不同,它要去重,同一个用户一天之内的多次访问请求只能计数一次。这就要求了每一个网页请求都需要带上用户的 ID,无论是登录用户还是未登录的用户,都需要一个唯一 ID 来标识。你也许马上就想到了一个 简单的解决方案:那就是 为每一个页面设置一个独立的 set 集合 来存储所有当天访问过此页面的用户 ID。但这样的 问题 就是:存储空间巨大: 如果网站访问量一大,你需要用来存储的 set 集合就会非常大,如果页面再一多.. 为了一个去重功能耗费的资源就可以直接让你 老板打死你;统计复杂: 这么多 set 集合如果要聚合统计一下,又是一个复杂的事情;基数统计的常用方法对于上述这样需要 基数统计 的事情,通常来说有两种比 set 集合更好的解决方案:第一种:B 树B 树最大的优势就是插入和查找效率很高,如果用 B 树存储要统计的数据,可以快速判断新来的数据是否存在,并快速将元素插入 B 树。要计算基础值,只需要计算 B 树的节点个数就行了。不过将 B 树结构维护到内存中,能够解决统计和计算的问题,但是 并没有节省内存。第二种:bitmapbitmap 可以理解为通过一个 bit 数组来存储特定数据的一种数据结构,每一个 bit 位都能独立包含信息,bit 是数据的最小存储单位,因此能大量节省空间,也可以将整个 bit 数据一次性 load 到内存计算。如果定义一个很大的 bit 数组,基础统计中 每一个元素对应到 bit 数组中的一位,例如:bitmap 还有一个明显的优势是 可以轻松合并多个统计结果,只需要对多个结果求异或就可以了,也可以大大减少存储内存。可以简单做一个计算,如果要统计 1 亿 个数据的基数值,大约需要的内存:100_000_000/ 8/ 1024/ 1024 ≈ 12 M,如果用 32 bit 的 int 代表 每一个 统计的数据,大约需要内存:32 * 100_000_000/ 8/ 1024/ 1024 ≈ 381 M可以看到 bitmap 对于内存的节省显而易见,但仍然不够。统计一个对象的基数值就需要 12 M,如果统计 1 万个对象,就需要接近 120 G,对于大数据的场景仍然不适用。概率算法实际上目前还没有发现更好的在 大数据场景 中 准确计算 基数的高效算法,因此在不追求绝对精确的情况下,使用概率算法算是一个不错的解决方案。概率算法 不直接存储 数据集合本身,通过一定的 概率统计方法预估基数值,这种方法可以大大节省内存,同时保证误差控制在一定范围内。目前用于基数计数的概率算法包括:Linear Counting(LC):早期的基数估计算法,LC 在空间复杂度方面并不算优秀,实际上 LC 的空间复杂度与上文中简单 bitmap 方法是一样的(但是有个常数项级别的降低),都是 O(Nmax)LogLog Counting(LLC):LogLog Counting 相比于 LC 更加节省内存,空间复杂度只有 O(log2(log2(Nmax)))HyperLogLog Counting(HLL):HyperLogLog Counting 是基于 LLC 的优化和改进,在同样空间复杂度情况下,能够比 LLC 的基数估计误差更小其中,HyperLogLog 的表现是惊人的,上面我们简单计算过用 bitmap 存储 1 个亿 统计数据大概需要 12 M 内存,而在 HyperLoglog 中,只需要不到 1 K 内存就能够做到!在 Redis 中实现的 HyperLoglog 也只需要 12 K 内存,在 标准误差 0.81% 的前提下,能够统计 264 个数据!这是怎么做到的?! 下面赶紧来了解一下!二、HyperLogLog 原理我们来思考一个抛硬币的游戏:你连续掷 n 次硬币,然后说出其中连续掷为正面的最大次数,我来猜你一共抛了多少次。这很容易理解吧,例如:你说你这一次 最多连续出现了 2 次 正面,那么我就可以知道你这一次投掷的次数并不多,所以 我可能会猜是 5 或者是其他小一些的数字,但如果你说你这一次 最多连续出现了 20 次 正面,虽然我觉得不可能,但我仍然知道你花了特别多的时间,所以 我说 GUN...。这期间我可能会要求你重复实验,然后我得到了更多的数据之后就会估计得更准。我们来把刚才的游戏换一种说法:这张图的意思是,我们给定一系列的随机整数,记录下低位连续零位的最大长度 K,即为图中的 maxbit,通过这个 K 值我们就可以估算出随机数的数量 N。代码实验我们可以简单编写代码做一个实验,来探究一下 K 和 N 之间的关系:public class PfTest {

static class BitKeeper {

private int maxbit;

public void random() {

long value = ThreadLocalRandom.current().nextLong(2L << 32);

int bit = lowZeros(value);

if (bit > this.maxbit) {

this.maxbit = bit;

}

}

private int lowZeros(long value) {

int i = 0;

for (; i < 32; i++) {

if (value >> i << i != value) {

break;

}

}

return i - 1;

}

}

static class Experiment {

private int n;

private BitKeeper keeper;

public Experiment(int n) {

this.n = n;

this.keeper = new BitKeeper();

}

public void work() {

for (int i = 0; i < n; i++) {

this.keeper.random();

}

}

public void debug() {

System.out

.printf("%d %.2f %d\n", this.n, Math.log(this.n) / Math.log(2), this.keeper.maxbit);

}

}

public static void main(String[] args) {

for (int i = 1000; i < 100000; i += 100) {

Experiment exp = new Experiment(i);

exp.work();

exp.debug();

}

}

}跟上图中的过程是一致的,话说为啥叫 PfTest 呢,包括 Redis 中的命令也一样带有一个 PF 前缀,还记得嘛,因为 HyperLogLog 的提出者上文提到过的,叫 Philippe Flajolet。截取部分输出查看://n n/log2 maxbit

34000 15.05 13

35000 15.10 13

36000 15.14 16

37000 15.18 17

38000 15.21 14

39000 15.25 16

40000 15.29 14

41000 15.32 16

42000 15.36 18会发现 K 和 N 的对数之间存在显著的线性相关性:N 约等于 2k更近一步:分桶平均如果 N 介于 2k 和 2k+1 之间,用这种方式估计的值都等于 2k,这明显是不合理的,所以我们可以使用多个 BitKeeper 进行加权估计,就可以得到一个比较准确的值了:public class PfTest {

static class BitKeeper {

// 无变化, 代码省略

}

static class Experiment {

private int n;

private int k;

private BitKeeper[] keepers;

public Experiment(int n) {

this(n, 1024);

}

public Experiment(int n, int k) {

this.n = n;

this.k = k;

this.keepers = new BitKeeper[k];

for (int i = 0; i < k; i++) {

this.keepers[i] = new BitKeeper();

}

}

public void work() {

for (int i = 0; i < this.n; i++) {

long m = ThreadLocalRandom.current().nextLong(1L << 32);

BitKeeper keeper = keepers[(int) (((m & 0xfff0000) >> 16) % keepers.length)];

keeper.random();

}

}

public double estimate() {

double sumbitsInverse = 0.0;

for (BitKeeper keeper : keepers) {

sumbitsInverse += 1.0 / (float) keeper.maxbit;

}

double avgBits = (float) keepers.length / sumbitsInverse;

return Math.pow(2, avgBits) * this.k;

}

}

public static void main(String[] args) {

for (int i = 100000; i < 1000000; i += 100000) {

Experiment exp = new Experiment(i);

exp.work();

double est = exp.estimate();

System.out.printf("%d %.2f %.2f\n", i, est, Math.abs(est - i) / i);

}

}

}这个过程有点 类似于选秀节目里面的打分,一堆专业评委打分,但是有一些评委因为自己特别喜欢所以给高了,一些评委又打低了,所以一般都要 屏蔽最高分和最低分,然后 再计算平均值,这样的出来的分数就差不多是公平公正的了。上述代码就有 1024 个 "评委",并且在计算平均值的时候,采用了 调和平均数,也就是倒数的平均值,它能有效地平滑离群值的影响:avg = (3 + 4 + 5 + 104) / 4 = 29

avg = 4 / (1/3 + 1/4 + 1/5 + 1/104) = 5.044观察脚本的输出,误差率百分比控制在个位数:100000 94274.94 0.06

200000 194092.62 0.03

300000 277329.92 0.08

400000 373281.66 0.07

500000 501551.60 0.00

600000 596078.40 0.01

700000 687265.72 0.02

800000 828778.96 0.04

900000 944683.53 0.05真实的 HyperLogLog 要比上面的示例代码更加复杂一些,也更加精确一些。上面这个算法在随机次数很少的情况下会出现除零错误,因为 maxbit = 0 是不可以求倒数的。真实的 HyperLogLog有一个神奇的网站,可以动态地让你观察到 HyperLogLog 的算法到底是怎么执行的:http://content.research.neustar.biz/blog/hll.html其中的一些概念这里稍微解释一下,您就可以自行去点击 step 来观察了:m 表示分桶个数: 从图中可以看到,这里分成了 64 个桶;蓝色的 bit 表示在桶中的位置: 例如图中的 101110 实则表示二进制的 46,所以该元素被统计在中间大表格 Register Values 中标红的第 46 个桶之中;绿色的 bit 表示第一个 1 出现的位置: 从图中可以看到标绿的 bit 中,从右往左数,第一位就是 1,所以在 Register Values 第 46 个桶中写入 1;红色 bit 表示绿色 bit 的值的累加: 下一个出现在第 46 个桶的元素值会被累加;为什么要统计 Hash 值中第一个 1 出现的位置?因为第一个 1 出现的位置可以同我们抛硬币的游戏中第一次抛到正面的抛掷次数对应起来,根据上面掷硬币实验的结论,记录每个数据的第一个出现的位置 K,就可以通过其中最大值 Kmax 来推导出数据集合中的基数:N = 2KmaxPF 的内存占用为什么是 12 KB?我们上面的算法中使用了 1024 个桶,网站演示也只有 64 个桶,不过在 Redis 的 HyperLogLog 实现中,用的是 16384 个桶,即:214,也就是说,就像上面网站中间那个 Register Values 大表格有 16384 格。而Redis 最大能够统计的数据量是 264,即每个桶的 maxbit 需要 6 个 bit 来存储,最大可以表示 maxbit = 63,于是总共占用内存就是:(214) x 6 / 8 (每个桶 6 bit,而这么多桶本身要占用 16384 bit,再除以 8 转换成 KB),算出来的结果就是 12 KB。三、Redis 中的 HyperLogLog 实现从上面我们算是对 HyperLogLog 的算法和思想有了一定的了解,并且知道了一个 HyperLogLog 实际占用的空间大约是 12 KB,但 Redis 对于内存的优化非常变态,当 计数比较小 的时候,大多数桶的计数值都是 零,这个时候 Redis 就会适当节约空间,转换成另外一种 稀疏存储方式,与之相对的,正常的存储模式叫做 密集存储,这种方式会恒定地占用 12 KB。密集型存储结构密集型的存储结构非常简单,就是 16384 个 6 bit 连续串成 的字符串位图:我们都知道,一个字节是由 8 个 bit 组成的,这样 6 bit 排列的结构就会导致,有一些桶会 跨越字节边界,我们需要 对这一个或者两个字节进行适当的移位拼接 才可以得到具体的计数值。假设桶的编号为 index,这个 6 bity 计数值的起始字节偏移用 offset_bytes 表示,它在这个字节的其实比特位置偏移用 offset_bits 表示,于是我们有:offset_bytes = (index * 6) / 8

offset_bits = (index * 6) % 8前者是商,后者是余数。比如 bucket 2 的字节偏移是 1,也就是第 2 个字节。它的位偏移是 4,也就是第 2 个字节的第 5 个位开始是 bucket 2 的计数值。需要注意的是 字节位序是左边低位右边高位,而通常我们使用的字节都是左边高位右边低位。这里就涉及到两种情况,如果 offset_bits 小于等于 2,说明这 6 bit 在一个字节的内部,可以直接使用下面的表达式得到计数值 val:val = buffer[offset_bytes] >> offset_bits # 向右移位如果 offset_bits 大于 2,那么就会涉及到 跨越字节边界,我们需要拼接两个字节的位片段:# 低位值

low_val = buffer[offset_bytes] >> offset_bits

# 低位个数

low_bits = 8 - offset_bits

# 拼接,保留低6位

val = (high_val << low_bits | low_val) & 0b111111不过下面 Redis 的源码要晦涩一点,看形式它似乎只考虑了跨越字节边界的情况。这是因为如果 6 bit 在单个字节内,上面代码中的 high_val 的值是零,所以这一份代码可以同时照顾单字节和双字节:// 获取指定桶的计数值

#define HLL_DENSE_GET_REGISTER(target,p,regnum) do { \

uint8_t *_p = (uint8_t*) p; \

unsigned long _byte = regnum*HLL_BITS/8; \

unsigned long _fb = regnum*HLL_BITS&7; \ # %8 = &7

unsigned long _fb8 = 8 - _fb; \

unsigned long b0 = _p[_byte]; \

unsigned long b1 = _p[_byte+1]; \

target = ((b0 >> _fb) | (b1 << _fb8)) & HLL_REGISTER_MAX; \

} while(0)

// 设置指定桶的计数值

#define HLL_DENSE_SET_REGISTER(p,regnum,val) do { \

uint8_t *_p = (uint8_t*) p; \

unsigned long _byte = regnum*HLL_BITS/8; \

unsigned long _fb = regnum*HLL_BITS&7; \

unsigned long _fb8 = 8 - _fb; \

unsigned long _v = val; \

_p[_byte] &= ~(HLL_REGISTER_MAX << _fb); \

_p[_byte] |= _v << _fb; \

_p[_byte+1] &= ~(HLL_REGISTER_MAX >> _fb8); \

_p[_byte+1] |= _v >> _fb8; \

} while(0)稀疏存储结构稀疏存储适用于很多计数值都是零的情况。下图表示了一般稀疏存储计数值的状态:当 多个连续桶的计数值都是零 时,Redis 提供了几种不同的表达形式:00xxxxxx:前缀两个零表示接下来的 6bit 整数值加 1 就是零值计数器的数量,注意这里要加 1 是因为数量如果为零是没有意义的。比如 00010101 表示连续 22 个零值计数器。01xxxxxx yyyyyyyy:6bit 最多只能表示连续 64 个零值计数器,这样扩展出的 14bit 可以表示最多连续 16384 个零值计数器。这意味着 HyperLogLog 数据结构中 16384 个桶的初始状态,所有的计数器都是零值,可以直接使用 2 个字节来表示。1vvvvvxx:中间 5bit 表示计数值,尾部 2bit 表示连续几个桶。它的意思是连续 (xx +1) 个计数值都是 (vvvvv + 1)。比如 10101011 表示连续 4 个计数值都是 11。注意 上面第三种方式 的计数值最大只能表示到 32,而 HyperLogLog 的密集存储单个计数值用 6bit 表示,最大可以表示到 63。当稀疏存储的某个计数值需要调整到大于 32 时,Redis 就会立即转换 HyperLogLog 的存储结构,将稀疏存储转换成密集存储。对象头HyperLogLog 除了需要存储 16384 个桶的计数值之外,它还有一些附加的字段需要存储,比如总计数缓存、存储类型。所以它使用了一个额外的对象头来表示:struct hllhdr {

char magic[4]; /* 魔术字符串"HYLL" */

uint8_t encoding; /* 存储类型 HLL_DENSE or HLL_SPARSE. */

uint8_t notused[3]; /* 保留三个字节未来可能会使用 */

uint8_t card[8]; /* 总计数缓存 */

uint8_t registers[]; /* 所有桶的计数器 */

};所以 HyperLogLog 整体的内部结构就是 HLL 对象头 加上 16384 个桶的计数值位图。它在 Redis 的内部结构表现就是一个字符串位图。你可以把 HyperLogLog 对象当成普通的字符串来进行处理:> PFADD codehole python java golang

(integer) 1

> GET codehole

"HYLL\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80C\x03\x84MK\x80P\xb8\x80^\xf3"但是 不可以 使用 HyperLogLog 指令来 操纵普通的字符串,因为它需要检查对象头魔术字符串是否是 "HYLL"。四、HyperLogLog 的使用HyperLogLog 提供了两个指令 PFADD 和 PFCOUNT,字面意思就是一个是增加,另一个是获取计数。PFADD 和 set 集合的 SADD 的用法是一样的,来一个用户 ID,就将用户 ID 塞进去就是,PFCOUNT 和 SCARD 的用法是一致的,直接获取计数值:> PFADD codehole user1

(interger) 1

> PFCOUNT codehole

(integer) 1

> PFADD codehole user2

(integer) 1

> PFCOUNT codehole

(integer) 2

> PFADD codehole user3

(integer) 1

> PFCOUNT codehole

(integer) 3

> PFADD codehole user4 user 5

(integer) 1

> PFCOUNT codehole

(integer) 5我们可以用 Java 编写一个脚本来试试 HyperLogLog 的准确性到底有多少:public class JedisTest {

public static void main(String[] args) {

for (int i = 0; i < 100000; i++) {

jedis.pfadd("codehole", "user" + i);

}

long total = jedis.pfcount("codehole");

System.out.printf("%d %d\n", 100000, total);

jedis.close();

}

}结果输出如下:100000 99723发现 10 万条数据只差了 277,按照百分比误差率是 0.277%,对于巨量的 UV 需求来说,这个误差率真的不算高。当然,除了上面的 PFADD 和 PFCOUNT 之外,还提供了第三个 PFMEGER 指令,用于将多个计数值累加在一起形成一个新的 pf 值:> PFADD nosql "Redis" "MongoDB" "Memcached"

(integer) 1

> PFADD RDBMS "MySQL" "MSSQL" "PostgreSQL"

(integer) 1

> PFMERGE databases nosql RDBMS

OK

> PFCOUNT databases

(integer) 6相关阅读Redis(1)——5种基本数据结构 - https://www.wmyskxz.com/2020/02/28/redis-1-5-chong-ji-ben-shu-ju-jie-gou/Redis(2)——跳跃表 - https://www.wmyskxz.com/2020/02/29/redis-2-tiao-yue-biao/Redis(3)——分布式锁深入探究 - https://www.wmyskxz.com/2020/03/01/redis-3/扩展阅读【算法原文】HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm - http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf参考资料【Redis 作者博客】Redis new data structure: the HyperLogLog - http://antirez.com/news/75神奇的HyperLogLog算法 - http://www.rainybowe.com/blog/2017/07/13/%E7%A5%9E%E5%A5%87%E7%9A%84HyperLogLog%E7%AE%97%E6%B3%95/index.html深度探索 Redis HyperLogLog 内部数据结构 - https://zhuanlan.zhihu.com/p/43426875《Redis 深度历险》 - 钱文品/ 著本文已收录至我的 Github 程序员成长系列 【More Than Java】,学习,不止 Code,欢迎 star:https://github.com/wmyskxz/MoreThanJava个人公众号 :wmyskxz,个人独立域名博客:http://wmyskxz.com,坚持原创输出,下方扫码关注,2020,与您共同成长!http://weixin.qq.com/r/tCrP1zHEJ2xYrXW093_m (二维码自动识别)非常感谢各位人才能 看到这里,如果觉得本篇文章写得不错,觉得 「我没有三颗心脏」有点东西 的话,求点赞,求关注,求分享,求留言!创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!编辑于 2020-03-02 17:13​赞同 11​​1 条评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录【More Than Java】学习之路学习,不止 Code..自己在学习时走过的路

浅谈嵌入式开发中的HAL层设计 - 知乎

浅谈嵌入式开发中的HAL层设计 - 知乎首发于单片机嵌入式软件开发经验分享切换模式写文章登录/注册浅谈嵌入式开发中的HAL层设计Clarence嵌入式软件工程师 RISC-V AI爱好者 GPU浅谈嵌入式开发中的HAL层设计1 关于HAL(Hardware Abstraction Layer)层硬件抽象层技术最初是由Microsoft公司为确保WindowsNT的稳定性和兼容性而提出的。针对过去Windows系列操作系统经常出现的系统死机或崩溃等现象,Microsoft总结发现,程序设计直接与硬件通信,是造成系统不稳定的主要原因。在得出这个结论的基础上,微软公司在WindowsNT上取消了对硬件的直接访问,首先提出了硬件抽象层(Hardware Abstraction Layer,简称HAL)的概念,硬件抽象层就是:“将硬件差别与操作系统其他层相隔离的一薄层软件,它是通过采用使多种不同硬件在操作系统的其他部分看来是同一种虚拟机的做法来实现的。“后来,这种HAL设计思路被一些嵌入式操作系统参考,其系统内核被分成两层,上层称为“内核(Kernel)”,底层则称为“硬件抽象层”。在EOS中,HAL独立于EOS内核;对于操作系统和应用软件而言,HAL是对底层架构的抽象。综合分析HAL层的代码,可以发现这些代码与底层硬件设备是紧密相关的。因此,可以将硬件抽象层定义为所有依赖于底层硬件的软件。即使有些EOS的HAL在物理上是与系统内核紧密联系的,甚至相互交叉的,但是从功能上可以从分层技术的角度去分析它 。显然,HAL层的根本设计目的是在于上层软件可以通过常规的通用接口访问MCU的某些资源,而不用去关心“是哪个MCU”在做的,模糊了最底层的函数实现。在嵌入式方面,ST公司在HAL层的应用方面推广较多,很典型的就是stm32cubemx,这个软件实际是和STM32的HAL库联系起来一起用的。因为HAL库提供了兼容性非常强的函数接口定义,所以代码生成器(stm32cubemx)可以很方便的自动生成代码,而不需要开发者一点一点的翻阅芯片手册,查看寄存器。因为工作需要,我开始思考“如何设计一个HAL层”的问题,参考了TI的CC26xx系列SDK的封装方法,于是撰写这个文章。2 HAL层的关键设计原则设计HAL层,应该需要忽略一些硬件实现细节,更加宏观的看待MCU模块提供的功能。 就好比,我们会去想象一个“台灯”,那么用HAL去抽象这个台灯的话,他一定要有“照明”的功能,还可以调节“亮度”。 那么这个台灯就简单的抽象好了,我不会去关心这个台灯用的什么灯泡,只要这个灯泡能亮就行。 于是,我有一个统一的HAL层函数 HAL_OPEN_LIGHT()以及HAL_ADJUST_LIGHT(LIGHT)函数,抽象了这个接口。3 具体的简单实现例子在C语言中会去这样做 typedef void (LIGHT_Open_Fxn) (LIGHT_Handle handle); typedef void (LIGHT_AdjustFxn) (LIGHT_Handle handle , uint16_t light_strength); typedef struct LIGHT_FxnTable_ { LIGHT_OpenFxn light_open_Fxn; LIGHT_AdjustFxn ligh_adjust_Fxn; } LIGHT_FxnTable; 上述代码就定义好了,HAL的基本接口。后面如何实现这个接口呢,C中这样去写。 void Light_open_led(LIGHT_Handle handle){ //具有特色的实现方式 } void Light_adjust_led(LIGHT_Handle handle , uint16_t light_strength){ //特别的实现方式 } LIGHT_FxnTable LIGHT={ Light_open_led, Light_adjust_led, }//完成HAL接口的实现 这样一来创建的LIGHT句柄就包含了可以灵活配置实现方法的函数指针列表。如果我有两家台灯厂家竞争,那么我只需要重写Light_open_led和Light_adjust_led函数即可兼容两个厂家的台灯特点。上层软件调用的始终都是LIGHT->LIGHT_OpenFxn() 和 LIGHT->Light_adjust_led()。 LIGHT_FxnTable LIGHT={ Light_open_led_A, Light_adjust_led_A, }//HAL接口重新实现 LIGHT_FxnTable LIGHT={ Light_open_led_B, Light_adjust_led_B, } //HAL接口重新实现..... 3 HAL层的优点很多时候我们其实希望开发者能够自下而上的构建工程,比如先硬件实现(HW),然后编写中间层软件(Driver),最后我们再编写最高层软件(Application)。 但是实际情况下,往往不是这样,而是替补,有可能是产品找到了cost-down的低成本主控,或者芯片升级,或者根本就是主控芯片断货了,在这种情况下,APP代码是不可能变动的,只能在APP的要求下,准备中间层软件(Drievr)和硬件实现。APP代码是一系列的用户操作逻辑,好比开灯关灯的摩斯密码,有自己一套的使用外设逻辑,它只会简单机械的按照之前的套路使用下层提供的外设。而往往绝大多数情况下HW,Driver的组合不能完全兼容APP的调用规范。 所以在这种情境下,HAL层就变的非常重要了,他在HW,Driver 与APP之间提供一个缓冲的作用。当APP本就是根据HAL设计的时候,需要更换底层的时候,移植HW,Driver以适配HAL 当APP还未设计出来的时候,按照标准的HAL库结构去开发APP,为后期兼容更多处理器做前期工作。总而言之,HAL层的设计就是为了能兼容更多的处理器型号,兼容更多外设的实现方式。4 最后写这些分享一下我对HAL的理解,因为工作需要在这方面查找了很多关于HAL的资料,上述的封装思路是借鉴了TI的CC26XX的SDK里面对HAL层的封装,感兴趣的可以下载SDK研究一下,后面可能会更加详细的介绍TI的封装方法。近来世界疫情弥漫,经济混沌,愿诸君平安,岁月静好。夏日的阳光照耀,所以坏事都会烟消云散的!发布于 2020-05-29 20:57嵌入式系统嵌入式开发嵌入式软件开发​赞同 83​​4 条评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录单片机嵌入式软件开发经验分享分享开发过程中的

如何评价steam《人间地狱》(Hell Let Loose)? - 知乎

如何评价steam《人间地狱》(Hell Let Loose)? - 知乎首页知乎知学堂发现等你来答​切换模式登录/注册Steam人间地狱(游戏)如何评价steam《人间地狱》(Hell Let Loose)?关注者224被浏览884,211关注问题​写回答​邀请回答​好问题 21​添加评论​分享​57 个回答默认排序篝火营地​游戏开发等 3 个话题下的优秀答主​ 关注《人间地狱》:打破定式的二战题材 FPS文/ @熊猫命 名不见经传的英国开发商 Black Matter Pty Ltd 在 2019 年 6 月 6 日正式推出他们的二战题材多人合作对战 FPS《人间地狱(Hell Let Loose)》,主打真实的战场环境以及高度依赖相互协作的战术玩法,支持最大 100 名玩家分成两队展开 50 VS 50的战斗,利用各种武器装备、战略战术在固定的地图上推进,同时还必须考虑补给线、视野、团队交流、步坦协同等等其他传统 FPS 游戏里很少出现的元素。除此之外,多样化的职业系统也给了玩家尝试战场上不同角色,从而获得完全不同游戏体验的机会。游戏的战场采用真实比例设计,不同的地图被分割为数量不一的正方形格子,四个格子组成一个战区,每一个战区里有包含一处据点。据点的种类多种多样,有制高点、补给基地、工地等等,进入据点后能让战区内识别人数乘以 2。而「识别人数」的概念是整个战斗活动的关键,只有人数更多的一方才能提升针对相邻战区的进度条,达到 100% 之后算作占领该战区。而通过与敌人的不断拉锯把战线向前推进,是取得战斗胜利的最主要手段。游戏中的战场地图按照大家多年来的习惯以及对这种对战类 FPS 的理解,一定是哪一边枪法更好、刚枪能力更强就越容易取得战场上的主动,但《人间地狱》就像是为了打破这种思维定式而生的。首先游戏非常重视不同兵种之间、队员之间的协作,比如一辆坦克需要一名军官、一名驾驶员、一名炮手兼装填手,总共三个人才能开动,可想而知具体的操作过程中如果这三个人相互配合得不好,根本无法形成战斗力。而坦克一旦被击伤,又只有步兵里的特殊兵种工兵才可以进行修理,所以步坦协同就成了非常必要的推进组合。侦查+狙击小队侦察组由一名侦察兵一名狙击手组成,但在战场上的主要作用只是「侦查」,只要能成功标记对方的步兵小队、坦克具体位置,给总部里的远程火炮提供坐标,就相当于完成了任务。能当个狙击手偷偷阴人当然也挺不错,但一来这游戏的狙击手人数有限,进入游戏之后不一定能抢到,二来真实的战场环境让你很难第一时间识别出远处的黑影究竟是敌人还是队友,远不如其他游戏里的「独狼」那么酸爽。指挥官角色属于对游戏流程已经有一定理解的老鸟玩家,他负责联络各个小队长掌握他们的位置和状态,因为只有他才能呼叫火力支援和空投补给。炮兵听上去似乎应该是最轻松的职业,只需要待在大后返根据前方的情报打打炮就行了,但实际上如果侦查情报不足根本不知道该往哪里打,而且还得时刻提防被人抄了老家。而在推进过程中最关键的大小复活点设置则只能由步兵小队来完成,当然第一时间在前线与敌人接战的、最容易被友方炮火打死的也是他们。另外步兵里面还包含功能性的兵种分组,包括医疗兵、工兵、火箭筒兵等等,同样是战场上不可或缺的力量。通过这些介绍相信大家也能看出来了,单独一名士兵在《人间地狱》的战场里能做的事情非常有限,面对敌方据点的强大火力压制,任你枪法再好也没有用武之地,必须依靠后方火炮发射烟雾弹之后一点一点的往前推进,而在这个过程中复活点设置是否合理、视野是否没有死角、有没有敌人的部队在侧翼伏击等问题都要考虑周全,团队协作的重要性与战场的真实与残酷体现在战斗的方方面面。从技术角度来说,虽然采用虚幻引擎开发的这款游戏在画面上摆脱了很多小成本产品固有的印象,而且常见的优化问题并不明显,网络情况也比较好,但还是在建模水准、贴图精度等方面存在一定的缺陷,整个战场环境乍一看还行,细节方面还有很大的提升空间。除此之外,尽管游戏上线不到 1 个月,而且本身没什么知名度,但网络 FPS 的「老朋友」外挂已经出现,导致部分认真玩游戏的人不得已外流到其他地区的服务器。但游戏的强交流属性导致队长、指挥官级别的人几乎必须要戴上耳麦进行语音指挥才能正常进行游戏,所以到海外去面临的一大挑战除了网速还有语言关。总的来说,可以看出制作组针对当下对战型 FPS 面临的问题有一些独到的解决办法,并且在这款还在不断完善的游戏中将很多创意都变成了现实,尽管在当前版本中也有部分职业游戏体验单一、一局战斗时间过长等问题,但根据官方公布的更新路线图,将来都会针对这些问题进行改良,期待《人间地狱》能够获得更多二战 FPS 爱好者的认可。发布于 2019-07-02 19:19​赞同 199​​14 条评论​分享​收藏​喜欢收起​就是不听话广告狗,国职双板指导员,新西兰单板一级教练​ 关注谢邀~自从苏拉的视频火了之后,一周末入坑了无数的新玩家。满大街的萌新被老鸟们收割得不要不要的,老鸟们在奥马哈海滩打出了70:2的单人战绩这还不是最好的,美军萌新甚至发出了“终于亲身感受到了D-day的绝望”的呼喊。也许这就是Hell Let Loose来迎接萌新们的方式,你可能爬着爬着就挨黑枪死了,跑着跑着就被空袭炸飞,走着走着就掉河里淹死了。总之应了那句自嘲的老话:如果我们穿越到了二战,估计活不过5分钟。“让你真正体验二战的残酷”,绝对是这个游戏的主旨之一。作为CS的20年老玩家,WOT金币车收集党,耻辱之日死忠粉,全战全系拥有者,彩虹六号600小时达人来回答一下,hell let loose给我最大的感受总结起来2个字:真实。我是一个对历史地理有点爱好的小菜鸡,以往看各种历史书籍,很大一部分都是跟战争捆绑在一起的。单兵作战,我了解。战略态势,我也了解。但以排为单位的作战战术,比如步兵班为啥要采用三三制这样的班组战斗队形,完全不明就里。Hell Let Loose给我解释了很多我以前搞不明白的战争问题:1、“一点两面”的原理——如果你没有面,左右两翼就会暴露,敌人就可以轻易打击你的侧后方。没有点,你就缺乏攻坚的能力,在Hell Let Loose里往往出现四面包围了一个点,20分钟愣是拿不下来的局面。2、攻坚的时候要展开战斗队形,乱麻麻的挤在一起往上冲,2挺机枪就能教你们做人。3、其他游戏步兵都是超人,Hell Let Loose里听得最多的就是步坦协同,步坦协同,步坦协同。没有坦克,步兵就是渣(昨天有一把FOY被对面3量虎式平行推进一推到底。),没有步兵,坦克分分钟就跪了。4、为啥要抢占高地,因为高地辐射的范围大。为啥渡口易守难攻,因为河面宽阔无遮挡,2挺机枪,1辆坦克就能守住桥梁,任你来多少死多少。5、为啥要搞三三制,你在Hell Let Loose里试下自己单独行动和小队一起行动,有多大的区别。6、我们经常听一句话是:如果端掉了敌人的指挥部,对方就群龙无首必输无疑。玩Hell Let Loose之前大家可能觉得有没有指挥都差不了多少。玩过就知道,TM没指挥怎么能行!7、穷则战术穿插,达则给老子炸!感觉战术穿插是刻在每个中国人骨子里面的基因。。。8、其他游戏的压制火力基本都是摆设,Hell Let Loose里,压制火力那真就是压制火力。一柄mp40都能够火力支援让你冒不了头。9、当你上了战场,还没搞清楚状况,几分钟后就被打死的情形。从1937-1945年每天都在发生。10、包围和溃败发生的时候,不管你是受过训练的老兵还是枪都不会用的菜鸡,死法都是一样的。11、一个普通的士兵在战场上,屁用没有。而一个牛逼的指挥,却能够扭转乾坤。所以历史只记得白起李牧彭德怀粟裕,不记得张三或者李四。12、步兵玩的是个射击游戏,指挥和小队长可能玩的是钢铁雄心,装甲玩家玩的是个WOT,工兵们可能玩的时候建筑游戏,就这样卖你100块你还嫌多?!?!2021.2.4补充我一直以为也就是火一周的事,结果这段时间各个平台主播UP越发的把更多的人拉进了坑,ASIA服务器从原来的4,5个变成了现在的光CN的都有20,30个。。。感觉接下来就有组战队打联赛的感觉了。(50 VS 50的电竞,想想都刺激)再补几条觉得HLL做得特别好的地方:距离感:距离感和射击抖动这点制作得非常好,包括跑步移动速度。随之而来的就是让玩家充分感受到了不同交战距离上武器的应用,你终于会发现电视剧里出神入化的手枪在大战场里除了用来自杀屁都没有用。50M以内,MP40和汤姆逊有良好的压制效果,50M以上就是98K和M1的天下了。你发现了想靠机瞄打中100M以外的移动目标,对普通人真是个很难的事情,别再动不动说什么800M外一枪爆头这种深井冰的话了。空降兵:看过《拯救大兵瑞恩》,你就觉得101这种部队投入进去不就是送死么。但是你玩过了HLL,你就会发现,在一个战场双方人力物力资源均等的前提下,空降兵就是打破双方战略平衡的那一把尖刀。进攻&防御:进攻方的伤亡永远远大于防御方的。你可能很难理解,但是玩过HLL你就会知道,这就是一个事实。防御方随便趴一个草丛,进攻方不牺牲4,5个人根本弄不死这个打一枪换一个地方的LYB。炮兵才是爸爸:如果对面有一个炮兵小队,附带了前线观察人员,绝对可以让你的游戏体验降到最低。没有什么是一炮弄不死的,如果有,那就再来一炮。推进速度:一个正面战场,在双方投入足够多的人力物力的时候,每天究竟应该推进多少公里?HLL会告诉你,每一个据点的时间差不多是30分钟。一天下来你能推进个2公里就已经非常不错了。(整个索姆河战役,战线一共只推进了10公里,双方伤亡130万人,大家可以借助HLL自行想象一下当时的场景了)资源:战斗打到最后,拼的都是国力,资源。耗尽了对方的资源,你就赢了。对的!说的就是你们这些不修资源的工程兵!编辑于 2021-02-04 14:03​赞同 222​​38 条评论​分享​收藏​喜欢

优秀的基数统计算法——HyperLogLog - 知乎

优秀的基数统计算法——HyperLogLog - 知乎首发于极客技术切换模式写文章登录/注册优秀的基数统计算法——HyperLogLog记得要让着本宝宝​FinvGroup 资深开发工程师为什么要使用 HyperLogLog?

在我们实际开发的过程中,可能会遇到这样一个问题,当我们需要统计一个大型网站的独立访问次数时,该用什么的类型来统计?

如果我们使用 Redis 中的集合来统计,当它每天有数千万级别的访问时,将会是一个巨大的问题。因为这些访问量不能被清空,我们运营人员可能会随时查看这些信息,那么随着时间的推移,这些统计数据所占用的空间会越来越大,逐渐超出我们能承载最大空间。

例如,我们用 IP 来作为独立访问的判断依据,那么我们就要把每个独立 IP 进行存储,以 IP4 来计算,IP4 最多需要 15 个字节来存储信息,例如:110.110.110.110。当有一千万个独立 IP 时,所占用的空间就是 15 bit*10000000 约定于 143MB,但这只是一个页面的统计信息,假如我们有 1 万个这样的页面,那我们就需要 1T 以上的空间来存储这些数据,而且随着 IP6 的普及,这个存储数字会越来越大,那我们就不能用集合的方式来存储了,这个时候我们需要开发新的数据类型 HyperLogLog 来做这件事了。

HyperLogLog 介绍

HyperLogLog(下文简称为 HLL)是 Redis 2.8.9 版本添加的数据结构,它用于高性能的基数(去重)统计功能,它的缺点就是存在极低的误差率。

HLL 具有以下几个特点:

能够使用极少的内存来统计巨量的数据,它只需要 12K 空间就能统计 2^64 的数据;

统计存在一定的误差,误差率整体较低,标准误差为 0.81%;

误差可以被设置辅助计算因子进行降低。

基础使用

HLL 的命令只有 3 个,但都非常的实用,下面分别来看。

添加元素

127.0.0.1:6379> pfadd key "redis"

(integer) 1

127.0.0.1:6379> pfadd key "java" "sql"

(integer) 1

相关语法:

pfadd key element [element ...]

此命令支持添加一个或多个元素至 HLL 结构中。

统计不重复的元素

127.0.0.1:6379> pfadd key "redis"

(integer) 1

127.0.0.1:6379> pfadd key "sql"

(integer) 1

127.0.0.1:6379> pfadd key "redis"

(integer) 0

127.0.0.1:6379> pfcount key

(integer) 2

从 pfcount 的结果可以看出,在 HLL 结构中键值为 key 的元素,有 2 个不重复的值:redis 和 sql,可以看出结果还是挺准的。

相关语法:

pfcount key [key ...]

此命令支持统计一个或多个 HLL 结构。

合并一个或多个 HLL 至新结构

新增 k 和 k2 合并至新结构 k3 中,代码如下:

127.0.0.1:6379> pfadd k "java" "sql"

(integer) 1

127.0.0.1:6379> pfadd k2 "redis" "sql"

(integer) 1

127.0.0.1:6379> pfmerge k3 k k2

OK

127.0.0.1:6379> pfcount k3

(integer) 3

相关语法:

pfmerge destkey sourcekey [sourcekey ...]

pfmerge 使用场景

当我们需要合并两个或多个同类页面的访问数据时,我们可以使用 pfmerge 来操作。

代码实战

接下来我们使用 Java 代码来实现 HLL 的三个基础功能,代码如下:

import redis.clients.jedis.Jedis;

public class HyperLogLogExample {

public static void main(String[] args) {

Jedis jedis = new Jedis("127.0.0.1", 6379);

// 添加元素

jedis.pfadd("k", "redis", "sql");

jedis.pfadd("k", "redis");

// 统计元素

long count = jedis.pfcount("k");

// 打印统计元素

System.out.println("k:" + count);

// 合并 HLL

jedis.pfmerge("k2", "k");

// 打印新 HLL

System.out.println("k2:" + jedis.pfcount("k2"));

}

}

以上代码执行结果如下:

k:2

k2:2

HLL 算法原理

HyperLogLog 算法来源于论文 HyperLogLog the analysis of a near-optimal cardinality estimation algorithm,想要了解 HLL 的原理,先要从伯努利试验说起,伯努利实验说的是抛硬币的事。一次伯努利实验相当于抛硬币,不管抛多少次只要出现一个正面,就称为一次伯努利实验。

我们用 k 来表示每次抛硬币的次数,n 表示第几次抛的硬币,用 k_max 来表示抛硬币的最高次数,最终根据估算发现 n 和 k_max 存在的关系是 n=2^(k_max),但同时我们也发现了另一个问题当试验次数很小的时候,这种估算方法的误差会很大,例如我们进行以下 3 次实验:

第 1 次试验:抛 3 次出现正面,此时 k=3,n=1;

第 2 次试验:抛 2 次出现正面,此时 k=2,n=2;

第 3 次试验:抛 6 次出现正面,此时 k=6,n=3。

对于这三组实验来说,k_max=6,n=3,但放入估算公式明显 3≠2^6。为了解决这个问题 HLL 引入了分桶算法和调和平均数来使这个算法更接近真实情况。

分桶算法是指把原来的数据平均分为 m 份,在每段中求平均数在乘以 m,以此来消减因偶然性带来的误差,提高预估的准确性,简单来说就是把一份数据分为多份,把一轮计算,分为多轮计算。

而调和平均数指的是使用平均数的优化算法,而非直接使用平均数。

例如小明的月工资是 1000 元,而小王的月工资是 100000 元,如果直接取平均数,那小明的平均工资就变成了 (1000+100000)/2=50500‬ 元,这显然是不准确的,而使用调和平均数算法计算的结果是 2/(1/1000+1/100000)≈1998 元,显然此算法更符合实际平均数。

所以综合以上情况,在 Redis 中使用 HLL 插入数据,相当于把存储的值经过 hash 之后,再将 hash 值转换为二进制,存入到不同的桶中,这样就可以用很小的空间存储很多的数据,统计时再去相应的位置进行对比很快就能得出结论,这就是 HLL 算法的基本原理,想要更深入的了解算法及其推理过程,可以看去原版的论文,链接地址在文末。

小结

当需要做大量数据统计时,普通的集合类型已经不能满足我们的需求了,这个时候我们可以借助 Redis 2.8.9 中提供的 HyperLogLog 来统计,它的优点是只需要使用 12k 的空间就能统计 2^64 的数据,但它的缺点是存在 0.81% 的误差,HyperLogLog 提供了三个操作方法 pfadd 添加元素、pfcount 统计元素和 pfmerge 合并元素。

参考文献

论文 HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm

本文使用 文章同步助手 同步发布于 2022-02-01 10:15​赞同 1​​添加评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录极客技术看极客技术,品人生百态 | 公号:小

一文理解 HyperLogLog(HLL) 算法 | 社区征文 - 文章 - 开发者社区 - 火山引擎

一文理解 HyperLogLog(HLL) 算法 | 社区征文 - 文章 - 开发者社区 - 火山引擎

We're sorry but react app doesn't work properly without JavaScript enabled. Please enable it to continue.文档备案控制台登录立即注册首页文章问答视频活动下载资源团队号镜像站一文理解 HyperLogLog(HLL) 算法 | 社区征文用户474807981238AI社区征文算法HyperLogLog(HLL) 算法是一种估算海量数据基数的方法,被广泛用于各个数据库产品中。

与精确的基数统计算法相比,HLL 具备可合并性 (mergeability) ,因而可以方便地对海量数据进行并行计算,被广泛地用于大数据多维分析场景中。例如分别统计一款 APP 每个小时的 UV 以及全天的 UV,这类问题就非常适合使用 HLL 算法。

本文将会由浅入深,从基本概念讲起,引导读者从直观上理解 HLL 算法背后蕴含的基本思想。

基数统计

基数 (Cardinality) 是指一个字段所包含的不同取值的个数,有时候也称为 Distinct Values,简写为 DV。

举个例子:

序列 [1, 2, 3, 4] 的基数为 4,因为包含 4 个不同的取值。

序列 [1, 2, 3, 1, 2] 的基数为 3,虽然包含 5 个元素,但其中的 1, 2 分别重复了一次。

最直观的基数统计方法是利用 HashSet:将序列中的所有值依次添加到 HashSet 中,最后统计 HashSet 中值的个数即可。用 Python 代码描述如下:

def get_dv(stream):

s = set()

for value in stream:

s.add(value)

return len(s)

既然如此,为什么我们不使用 HashSet 来计算基数呢?

原因在于计算成本。当要统计的数据非常多时,HashSet 将会占用很大的内存,以至于资源耗尽也无法完成计算,这种情况在大数据场景下非常常见。在 HashSet 的基础上,有一个可以节省资源的改进方案,就是采用 bitmap,但 bitmap 只是把问题延缓了,仍然没有根本性地解决问题。

事实上,我们统计基数时往往并不要求分毫不差,只需要给出一个具有误差边界的粗略值即可。那么在这种前提下能否节省计算资源呢?

HyperLogLog(HLL) 就是这样一种算法,可以在计算结果的精确程度和资源占用之间取得一种平衡。

下面让我们从一些浅显的问题着手,逐步揭开 HLL 算法的神秘面纱。

从概率视角看计数方法

常规的计数方法会维护一个列表,每到来一条数据记录一下。这种计数是精确的,但代价是必须维护一个越来越长的列表。

概率论为我们提供了另外一种看待计数的视角,即:

观测到小概率事件发生(概率 p) → 类似的事情重复过很多次了(次数 N)

其中蕴含着一个粗略的定量关系:

N = 1/p

举个例子:

在摇骰子猜大小的游戏中,三个骰子同时为 6 点的概率很小,为 1/(6^3)。假如在某场游戏中摇出了三个 6 点,猜猜一共摇了几次?

答:大概 6^3=216 次

更进一步的例子:

有一个抛硬币游戏,规则如下:玩家每次抛掷一枚均匀的硬币,正面与反面朝上的概率均为 1/2,每次抛掷都记一分。如果正面朝上,则继续抛掷;如果反面朝上,则游戏结束。

问1:在一局游戏中得 5 分的概率是多少?

答1:得 5 分意味着抛掷序列为「正正正正反」,概率为 (1/2)^5 = 1/32

问2:在玩了若干局游戏后,最高得分是 5 分,请问玩了多少局?

答2:大概 32 局

如果把硬币的正反两面分别记作 0、1 的话,那么 HyperLogLog 的计数原理就呼之欲出了:

对于每一条待统计的数据(例如 user_id),计算其 hash 值并写成二进制形式(0-1 串),然后将其看作一局抛硬币游戏的记录。其中:

0 代表硬币正面朝上。

1 代表硬币反面朝上。

例如 hash( uid_345678 )=00010010,意味着这局抛硬币游戏出现连续 3 次正面朝上,第 4 次反面朝上,游戏结束,最终得分为 4。第 4 位以后的序列不影响得分,可以忽略。

按照上述步骤,计算每条数据对应的「得分」,并找出其中的最高分 μ。那么这组数据的基数的期望为:

N = 2^μ

这就是利用概率论来估算基数所依据的基本原理。

在上述过程中涉及了一个重要步骤,就是将每个待观察的数据进行 hash 操作。为什么需要 hash 操作,而不是直接观察数据本身对应的二进制串呢?

这是因为游戏要求每次取 0 或 1 的概率是均等的,都是 0.5(这样整局游戏是一个伯努利过程)。换言之,要确保观察的 0-1 串足够随机才行。如果不做 hash 的话则无法保证随机性,例如对于 int 类型的数据,较小的值如 0、1、2 的二进制串中包含很长的连续 0,导致得分很高,这显然是错误的。

HLL 中实际使用的 hash 算法为 MurmurHash,其主要优势是随机性强和快速。

此外,比特币中使用 hash 值的前导零的个数来定义挖矿时的难度值 (difficulty) ,其蕴含的思想是完全相同的。前导零个数越多,意味着要尝试的 hash 计算次数越多,对应着基数越大,其工作量/难度也越高。

MVP 版基数估计算法

根据上面的讨论,使用 Python 描述一个简单的基数估计算法如下:

def get_dv(stream):

max_z = 0

for value in stream:

h = murmur_hash(value)

z = leading_zeros_count(h)

if z > max_z:

max_z = z

return 1 << (max_z + 1)

这个算法足够简单,而且理论上是没问题的。但实际上在数据量较小的情况下误差非常大,因为前导零个数的偶然性太大了。就好比抛硬币总是容易出现某一次运气爆棚的情况,严重拉高总体的估算值。

设想一下,假设仅输入一条数据,这个数据刚好有 1 个前导零,那么最终估计出的基数为 2^(1+1)=4,如果刚巧遇到更多的前导零,那么偏差会更大。最关键的是,这些偏差无法靠算法本身来控制,准确度全靠运气。

LogLog 算法

为了解决 MVP 算法不稳定、运气成分大的问题,一种最简单的思路就是「分拆计算求平均值」,也就是把输入数据均分为 m 份(称为桶),每一个桶分别应用 MVP 算法,最终得分 μˉ 为各桶得分的平均值。这就是 LogLog 算法所采用的思路,LogLog 是早于 HyperLogLog 诞生的一种算法。

LogLog 算法的计算公式可表示为:

其中,m 为分桶个数,μˉ 为各桶最高得分的平均值,α 为修正系数,用于修正算术平均数带来的系统偏差。α 的计算规则如下:

根据算法的特点,通常将分桶数 m 设为 2 的整数次幂。例如 m=64=2^6,此时可以通过 hash 值的前 6 个 bit 来表示桶编号。从第 7 个 bit 开始统计前导零个数。

HyperLogLog 算法

LogLog 算法通过「分桶求平均值」的方式提高了估算结果的稳定性,使得算法更能抵御偶然性带来的影响。

但这么做仍然不够,因为算术平均数有一个天然的缺陷,就是容易受到极大/极小值的影响,一个离群点可能把最终结果严重带偏。例如老板月入 100000 元,9 个员工均月入 3000 元,那么平均收入就是 (100000+3000*9)/10=12700 元,这距离群里中的大多数(即 9 个员工)相差甚远,不能反映普遍情况。

如果将算术平均数改为调和平均数就可以解决这个问题,调和平均数的计算公式如下:

使用调和平均数计算出的平均收入为 10/(1/100000+9/3000)=3322,比较接近群体中的普遍情况。

HyperLogLog 算法对于 LogLog 算法的重要改进就是把算术平均数改成了调和平均数。同时,HLL 不是先求平均得分,再计算指数(因为这会导致离群点的效应指数级放大),而是先计算出每个桶的基数,然后求调和平均数。

HLL 统计基数的公式如下:

在实际使用中,为了提高小样本的准确度,HLL 在上述公式计算结果的基础上还进行了一次修正。完整计算流程参见下图:

前面提到过,分桶数越多越能抵御偶然效应带来的影响,使得基数估计的结果更准确。那么可以想到,HLL 算法的估算精度(用相对标准误差 RSD 来表示)与分桶数 m 之间存在负相关关系。其定量关系如下:

有了这个关系,我们可以轻易地通过想要达到的误差精度来决定分桶的个数。

合并

HLL 算法的一个重要特点是可合并性,使其能够预先统计各个子集的基数,然后汇总得到总体基数,极大地提高了统计效率。

HLL 结构体的合并过程非常简单,这是因为每个 HLL 结构体本质上就是一个桶数组。假设要将桶数组 a 和 b 合并成桶数组 c,只需要从 a、b 的对应位置取最大值即可,使用 Python 代码描述如下:

def hll_merge(a, b):

m = len(a)

c = [0]*m

for i in range(m):

c[i] = max(a[i], b[i])

return c

合并后的桶数组按照上文中的公式估算基数即可。

示例

Rob Grzywinski 创建了一个 HyperLogLog 算法的 demo 网站,可以直观地理解算法的计算过程。

下图中显示,输入数据 value=8188163,其 MurmurHash 值为 84796297,二进制串见图。此外,图中 m=64 代表有 64 个桶,每个桶中的最高得分维护在一个表格中。

二进制串的最后 6 bit 用于表示桶 (Register) 编号,即 001001(9),所以当前数据划到第 9 桶。为什么用 6 bit 表示桶编号?因为这样刚好足够区分 64 个桶。如果要求桶数更多,则相应地需要更多 bit。

紧接着的 15 bit 用于统计得分(从右端开始),本例中得分为 2,因此第 9 个桶中记录 2。为什么只统计 15 个 bit 呢?因为工程实现中 register 结构体是有空间限制的,此处每个 register 占用 4 bit,记录范围为 0~15,所以能容纳的最大数字就是 15,如果得分大于 15 就记录不上了。

可以想象,每个桶占用 4 bit 的话能够统计的数据量非常有限,当所有桶的得分都为 15 时达到上限,约为 0.709*64*2^15=1486880 个。但考虑到整个 HLL 结构体仅占用了 64*4bit=32byte 的存储,空间效率是非常惊人的。

从上面的推导中可以看到,一个 HLL 结构体所能统计的基数范围与它占用的空间存在两重 Log 关系,这也是算法被称为 LogLog 的原因。

回到上面的例子就是,基数容量 1486880 ∝ 2^(2^4),其中的"4"代表每个桶占用 4bit 空间。

业界案例

在工程实践中,HLL 结构体的桶数和容量通常是可调参数,当数据量增大或者要求更高的精确度时,可以调高容量。

下面列出一些业界使用 HLL 算法的例子:

1. Apache DataSketch

Apache DataSketch 算法族中包含 HyperLogLog 的实现,该算法族被广泛用于许多大数据基础组件中,用于支持基数、分位数等的快速计算。例如:

Hive/Spark 通过官方 UDF/UDAF 的方式使用 DataSketch;

Apache Druid 通过官方插件的形式引入 DataSketch 扩展;

PostgreSQL 通过插件形式引入 DataSketch 算法。

2. Redis

Redis 中使用 PFCOUNT 命令来调用 HLL 算法。

其 HLL 结构使用了 2^14=16384 个桶,hash 值采用 64bit 表示,除了桶编号之外剩余的 50 bit (64-14=50) 全部用于统计得分。为了确保桶中记录的分数最大范围高于 50,每个桶需要占用 6 bit 空间(2^6>50)。这样,总体的空间占用为 16384*6bit=12KB。

当数据量很少时会存在大量的空桶,此时出于优化目的,可以借助稀疏存储的表示方法来压缩空间,能够取得数倍到上千倍的压缩率。

3. ClickHouse

ClickHouse 中的 uniq 函数背后采用的是 HLL 算法。

4. Doris

Doris 中 approx_count_distinct 函数背后采用的也是 HLL 算法。原理类似,不再赘述。

延伸阅读

HLL 算法中每个桶仅记录该桶中最大的得分,而忽略其他得分,因此绝大多数数据完全没有留下任何痕迹就被丢弃了,但最终估计出来的结果却能体现出这些数据的存在,有些匪夷所思。其实这一点符合信息论的思想,即概率越小的事件所携带的信息量越大(S=-log2p)。由于得分最高的数据出现的概率最低,因此携带的信息量最大,意味着我们仅捕获海量数据中携带最大信息量的数据,而丢弃其他信息量较少的数据。增加分桶个数可以让捕获到的信息量线性增长,因此能够提高最终的精度。

通过概率论来计数的基本思想是根据「实验观察」与「概率理论」反推出「背后的事实」,而不是直接研究「背后的事实」,这种思想除了用于 HLL 算法之外,还见于其他很多地方,例如:利用蒙特卡洛方法估算圆周率、太阳升起问题。

参考资料

论文:HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm

博客:探索HyperLogLog算法(含Java实现)

Demo:Sketch of the Day: HyperLogLog — Cornerstone of a Big Data Infrastructure

7582101点赞评论收藏关于作者用户474807981238相关产品评论未登录看完啦,登录分享一下感受吧~暂无评论

HyperLogLog(HLL)介绍-CSDN博客

>

HyperLogLog(HLL)介绍-CSDN博客

HyperLogLog(HLL)介绍

最新推荐文章于 2024-03-06 15:11:41 发布

来自宇宙的曹先生

最新推荐文章于 2024-03-06 15:11:41 发布

阅读量199

收藏

点赞数

文章标签:

java

开发语言

idea

spring

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/m0_54187478/article/details/133923910

版权

1.何为HyperLogLog(HLL)

HyperLogLog(HLL)是一种用于近似计数的数据结构,通常用于大数据集合中的元素唯一性统计。HLL 的核心思想是将每个元素映射到一个位图中,并通过统计位图中前导零(leading zeros)的数量来估计唯一元素的数量。HLL的主要用途是估算一个数据集中不同元素的数量,而不是准确计数。HLL的误差率通常在0.81%左右。这个误差率是根据统计学的理论和数学计算得出的,与HLL的参数有关。这对于大规模数据集合的处理非常有用,因为传统的方法可能会消耗大量的内存和计算资源。

以下是HyperLogLog的一些关键概念:

基数估计: HLL主要用于估计一个数据集中不同元素的数量,这个数量通常被称为基数。估算基数是HLL的主要目标。 哈希函数: HLL使用哈希函数将元素映射到不同的桶(buckets)中。这些桶通常是一个大的位数组。 位数组: HLL的核心数据结构是一个位数组,其中每个位代表一个桶。每个桶通常包含一个0或1的值。 稀疏编码: 为了节省内存,HLL通常使用稀疏编码技术,只存储非零桶的信息,而其他桶都被视为零。 并集与交集: HLL支持计算多个HLL数据结构的并集和交集,这对于合并不同数据集的估算基数非常有用。 误差率: HLL的估算结果会有一定的误差率,通常在0.81%左右。这意味着估算的基数可能略有偏差,但通常可以在可接受的范围内。

HLL 使用了哈希函数、位操作和统计学方法,使得它能够在不存储所有元素的情况下,估计一个数据集的基数。虽然它是一个概率性数据结构,但在大多数情况下,它提供了接近准确的估计。

H- LL适用于各种场景,包括网络流量分析、大数据处理、唯一用户统计、查询优化等。其优点在于占用较小的内存空间,适用于大规模数据集合,同时提供了估算基数的功能,以便进行统计和分析。

HyperLogLog是一种有趣且有用的数据结构,用于解决估算唯一元素数量的问题,通常与NoSQL数据库(如Redis)一起使用,以便在处理大数据集时执行快速且节省内存的计数操作。

2.HyperLogLog(HLL)使用场景

HyperLogLog (HLL) 用于估计大规模数据集合中不同元素的数量。这些估算可能不是完全准确的,但通常具有可接受的误差范围。下面是一些HyperLogLog在实际场景中的使用示例:

唯一用户计数: 在大规模的网络应用中,你可能希望估计不同访问者或用户的数量。使用HLL可以在占用较少内存的情况下快速估计唯一用户的数量,这对于在线广告、社交媒体分析等领域非常有用。 网络流量分析: 在网络流量分析中,你可能需要了解不同IP地址的数量,以便监测恶意活动或评估流量。HLL可用于估算不同IP地址的数量,而不需要维护一个庞大的数据结构。 查询优化: 数据库查询优化中,HLL可用于估算不同值的数量,而无需扫描整个数据集。这对于大型数据表的查询优化非常有用。 广告点击率估算: 在广告分析中,HLL可以估算广告点击的唯一用户数量,以便衡量广告效果。 社交网络分析: 在社交网络中,HLL可用于估计不同用户的朋友数量或社交网络的规模,而无需跟踪每个用户的详细信息。 推荐系统: 在个性化推荐系统中,HLL可用于估算不同商品或兴趣点的数量,以帮助生成推荐。 实时数据处理: 对于实时数据流处理,HLL可以用于在数据流中估算唯一元素的数量,以便进行实时分析和决策。

需要注意的是,HLL适用于需要估算不同元素数量的场景,但不适用于需要准确计数的场景。其优势在于占用较少内存,适用于大规模数据集合。而在需要精确计数的情况下,应该使用传统的数据结构,如哈希表或数据库。

3.HyperLogLog优缺点

优点:

占用内存极少: HLL 能够估计大型数据集的唯一元素的数量,但占用的内存量非常有限。这使得它非常适合处理大规模数据,尤其是在内存有限的环境中。 高效计算: 添加新元素或执行合并操作都非常高效。计算基数(唯一元素的数量)的时间复杂度是 O(1)。 数据隐私性: HLL 不存储实际的元素值,只存储用于计算基数估计的信息,这有助于保护数据的隐私。 并行计算: HLL 可以方便地分布式计算,允许多个节点计算部分基数,然后合并结果。 容忍小规模误差: HLL 的误差率通常为 1% 左右,对于大数据集,这个误差率通常是可以接受的。

缺点:

无法提供精确结果: HLL 是一种概率性数据结构,它提供了对唯一元素数量的估计,但不能提供精确的结果。因此,如果你需要精确计数,HLL 不是最佳选择。 不适用于小数据集: 当数据集很小(例如,元素数量少于 1000)时,HLL 的误差率可能变得不可接受。 不支持删除操作: HLL 不支持从数据结构中删除元素。一旦添加到 HLL 中的元素就无法删除。 内存使用不稳定: HLL 的内存使用量随估计的基数数量而变化。较小的基数需要更少的内存,但较大的基数需要更多内存。这种内存使用不稳定性需要考虑。

总之,HyperLogLog 是一种非常有用的数据结构,特别适合处理大规模数据集的基数估计问题,但它不适用于所有类型的应用程序。在选择是否使用 HLL 时,需要仔细考虑你的应用需求和性能要求。

4.使用HyperLogLog(HLL)统计网站日IP访问量(UV)

下面是一个示例Java Service层的代码,用于统计某个网站某一日的UV,使用Redis中的HyperLogLog数据结构。

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.HyperLogLogOperations;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Service;

import java.util.List;

@Service

public class UvCountService {

private final RedisTemplate redisTemplate;

@Autowired

public UvCountService(RedisTemplate redisTemplate) {

this.redisTemplate = redisTemplate;

}

public void logUserVisit(String date, String userId) {

String uvKey = "uv:" + date; // 使用日期作为键,每日一个键

HyperLogLogOperations hyperLogLogOps = redisTemplate.opsForHyperLogLog();

hyperLogLogOps.add(uvKey, userId); // 将用户ID添加到HyperLogLog中

}

public long getUniqueVisitors(String date) {

String uvKey = "uv:" + date; // 使用日期作为键

HyperLogLogOperations hyperLogLogOps = redisTemplate.opsForHyperLogLog();

Long uvCount = hyperLogLogOps.size(uvKey); // 获取HyperLogLog的大小,即UV数量

return uvCount != null ? uvCount : 0;

}

public long getDailyUvCount(String date) {

return getUniqueVisitors(date);

}

public long getWeeklyUvCount(String startDate, String endDate) {

// 获取一周内的UV数量,你可以根据需要自行实现

long totalUvCount = 0;

List dateRange = DateUtils.getDateRange(startDate, endDate);

for (String date : dateRange) {

totalUvCount += getUniqueVisitors(date);

}

return totalUvCount;

}

}

这个示例中,我们使用Spring的RedisTemplate来与Redis进行交互,具体的HyperLogLog操作通过opsForHyperLogLog进行。你可以调用logUserVisit方法来记录用户访问,然后使用getUniqueVisitors方法获取某一日的UV数量。此外,还提供了getDailyUvCount和getWeeklyUvCount方法来获取每日和每周的UV数量。

优惠劵

来自宇宙的曹先生

关注

关注

0

点赞

0

收藏

觉得还不错?

一键收藏

知道了

0

评论

HyperLogLog(HLL)介绍

HLL适用于需要估算不同元素数量的场景,但不适用于需要准确计数的场景。总之,HyperLogLog 是一种非常有用的数据结构,特别适合处理大规模数据集的基数估计问题,但它不适用于所有类型的应用程序。HyperLogLog是一种有趣且有用的数据结构,用于解决估算唯一元素数量的问题,通常与NoSQL数据库(如Redis)一起使用,以便在处理大数据集时执行快速且节省内存的计数操作。社交网络分析: 在社交网络中,HLL可用于估计不同用户的朋友数量或社交网络的规模,而无需跟踪每个用户的详细信息。

复制链接

扫一扫

go-hll:HyperLog登录golang

05-17

请参阅Stefan Heule,Marc Nunkesser和Alex Hall的HyperLogLog。 Ø序列化 无需序列化/反序列化hll。 一切都存储在一个字节片中,该字节片可以进行内存映射,并按原样通过网络传递。 与论文的区别: 稀疏表示。 此...

Hyperloglog应用、原理和性能分析

m0_37558251的博客

04-10

3144

1.应用

Hyperloglog提供不精确的去重计数方案,如统计一个网站的UV(独立访客数),同一个用户一天之内的多次请求只能计数一次,如果使用set集合来统计的话,会非常浪费存储空间。

虽然不精确但是也不是非常不精确,标准误差是0.81%。

2.原理

1.抛硬币问题

设想一个抛硬币的问题,假如你抛了很多次硬币,最多出现了两次连续的反面,我敢打赌你抛硬币的总次数不会太多,相反,如果你和我...

参与评论

您还未登录,请先

登录

后发表或查看评论

redis中HyperLogLog使用(统计数据)

zpflwy1314的博客

12-14

1924

HyperLogLog是Redis中的高级数据结构,它主要用于对海量数据(可以统计2^64个数据)做基数统计(去重统计数量)。它的特点是速度快,

占用空间小(12KB)。但是计算存会在误差,标准误差为0.81%。HyperLogLog只会根据输入元素来计算基数,而不会储存输入元素本身,

所以他并不能判断给定的元素是否已经存在了。适合用来对海量数据进行unique统计,对内存占用有要求,而且还能够接受一定的错误率的场景。

对于union操作由于是O(N),在海量数据层面需要注意慢查询问题

基本指令

pfadd

探索Redis特殊数据结构:HyperLogLog在基数统计中的应用

Hanko的专栏

01-09

993

Redis官方提供了多种数据类型,除了常见的String、Hash、List、Set、zSet之外,还包括Stream、Geospatial、Bitmaps、Bitfields、Probabilistic(HyperLogLog、Bloom filter、Cuckoo filter、t-digest、Top-K、Count-min sketch、Configuration)和Time series。这些数据类型在Redis的数据结构中发挥着各自独特的作用。

比bitmap节省近1000倍内存的数据结构算法——HyperLoglog

m0_38041658的博客

05-08

295

比bitmap节省近1000倍内存的数据结构/算法——HyperLoglog

【进阶篇】2.2 三分钟了解Redis HyperLogLog 数据结构

日拱一卒无有尽,功不唐捐终入海

07-16

687

HyperLogLog算法是一种非常有用的数据结构和算法,它可以在很小的空间内估计一个集合中不重复元素的数量,具有很高的效率和精度,非常适合用于大数据量的计数场景。在使用时,需要注意HyperLogLog算法的概率特性,以及对误差的容忍度,才能得到最好的效果。答:Redis HyperLogLog是一种基数估计算法,它可以在只使用很少的内存空间的情况下,近似地估计一个集合中不重复元素的数量。

HLL 算法(HyperLogLog)

eafun_888的博客

03-07

2644

HyperLogLog,下面简称为HLL,它是 LogLog 算法的升级版,作用是能够提供不精确的去重计数。存在以下的特点:

代码实现较难。

能够使用极少的内存来统计巨量的数据,在 Redis 中实现的 HyperLogLog,只需要12K内存就能统计2^64个数据。

计数存在一定的误差,误差率整体较低。标准误差为 0.81% 。

误差可以被设置辅助计算因子进行降低。

特点--关键...

详解 impala的ndv()函数 原理(HyperLogLog (HLL))、使用方法、精度误差及替换方案

samur2的博客

02-15

2292

背景

最近使用impala做查询的时候,遇到一个sql 中使用多个列 count(distinct) 查询导致报错的情况,报错内容如下:

org.apache.hive.service.cli.HiveSQLException: AnalysisException: all DISTINCT aggregate functions need to have the same set of parameters as count(DISTINCT ); deviating function: count(DI

redis数据结构及应用举例三之HyperLogLog

lonely_baby的博客

02-23

199

HyperLogLog算法的计数精度取决于哈希函数和子集划分的数量,如果哈希函数的分布比较均匀,并且子集的数量足够大,那么HyperLogLog的计数精度可以达到比较高的水平。HyperLogLog算法的基本思路是通过哈希函数将每个元素映射到一个指定的区间内,然后统计区间内不同元素的数量,从而得到集合中不同元素的数量。在统计过程中,为了降低内存的占用量,HyperLogLog采用了随机化采样的方式,将集合划分为多个子集,每个子集进行单独的统计,最后对所有子集的计数结果进行加权平均,得到最终的统计结果。

玩转Redis-HyperLogLog统计微博日活月活

zxiaofan.com

07-19

1514

《玩转Redis》系列文章主要讲述Redis的基础及中高级应用。本文是《玩转Redis》系列第【9】篇。

日活数据统计面临哪些挑战?

HyperLogLog必知,HyperLogLog和Sets的区别,HyperLogLog命令对比分析,HyperLogLog命令注意事项。HyperLogLog的应用场景

mysql-hll:用于 MySQL 的 HyperLogLog UDF

06-13

用于 MySQL 的 HyperLogLog UDF例子增量积累 CREATE TABLE ` uniqueUsersPerDay `( ` day ` DATE PRIMARY KEY , ` hll ` BLOB); SET @bits = 14 ; -- set storage size to 2^@bits bytes (standard error 0.81%)SET ...

hll:用于C编程语言的HyperLogLog库

05-06

适用于C语言的HyperLogLog库 参见 例子 /* * File: examples/rnd.c */ # include # include # include " ../src/hll.h " int main ( int argc, char *argv[]) { long i; struct HLL hll; if ( hll_init (&...

postgresql-hll:PostgreSQL扩展添加HyperLogLog数据结构作为本机数据类型

05-06

HyperLogLog是一种固定大小的,类似于集合的结构,用于以可调的精度进行不同的值计数。 例如,在1280个字节中, hll可以估计数百亿个不同值的计数,而误差只有百分之几。 除了提出的算法外,还增加了此实现,以...

go-hll:实施Hyper Log Log

04-29

去 实施Hyper Log Log 安装 go get github . com / mtchavez / go - hll / hll 用法 创建新的 具有所需错误的新超级日志日志表 ... hll . Add ( "foo" ) } 新增中 在表中添加一些文字 package main import ( "github.

LeetCode hot100-4

alike_meng的博客

03-04

481

LeetCode热题100第4题

Java自学day4

nanshenchao的博客

03-03

1207

数字进行运算时,数据类型不一样不能运算,需要一样的,才能运算。类型转换的分类:1.隐式转换(自动类型提升):取值范围小的数值转向取值范围大的数值2.强制转换:取值范围大的数值转向取值范围小的数值如果把一个取值范围大的数值,赋值给取值范围小的变量。是不允许直接赋值的。如果一定要这么做就需要加入强制转换格式:目标数据类型 变量名 = (目标数据类型)被强转的数据;

【Web】浅聊JDBC的SPI机制是怎么实现的——DriverManager

最新发布

uuzeray的博客

03-06

963

【Web】浅浅地聊JDBC java.sql.Driver的SPI后门-CSDN博客上篇文章我们做到了知其然,知道了JDBC有SPI机制,并且可以利用其Driver后门这篇文章希望可以做到知其所以然,对JDBC的SPI机制的来源做到心里有数。

java-初级项目实战-(swing篇)-羊了个羊(动态交互,)

qq_61549190的博客

03-04

468

判断t方格是否压住b方格,就要保证t方格的左上的黑点始终在蓝框内。即t的 x坐标(x1,x2)之间,y(y1,y2)之间。将下方图片添加到一个盒子中(简单的添加)添加一个新的集合,用于存储下方的图片。蓝框为b方格的宽度和高度的2倍,x1的x即为b的左上点x-其宽度。y1的y即为b的左上点y-其高度。y2的y即为b的左上点y+其高度。x2的x即为b左上点x+其宽度。t方格的黑点设置为(x,y)先检测2张牌是否压住。

RedisHyperLogLog的使用方法

05-12

Redis的HyperLogLog是一种基数算法,用于估算一个集合中的元素数量。它的优点是占用空间小,计算速度快,但是在精度上有一定的误差。使用方法如下:

1. 添加元素

可以使用 PFADD 命令向 HyperLogLog 中添加元素。例如,向 key 为 hll 的 HyperLogLog 中添加元素 a、b、c:

```

PFADD hll a b c

```

2. 统计基数

可以使用 PFCOUNT 命令统计 HyperLogLog 中元素的基数。例如,统计 key 为 hll 的 HyperLogLog 中元素的基数:

```

PFCOUNT hll

```

3. 合并 HyperLogLog

可以使用 PFMERGE 命令合并多个 HyperLogLog。例如,将 key 为 hll2、hll3 的 HyperLogLog 合并到 key 为 hll1 的 HyperLogLog 中:

```

PFMERGE hll1 hll2 hll3

```

需要注意的是,合并的 HyperLogLog 必须属于同一种类型的数据,否则会返回错误信息。

4. 清空 HyperLogLog

可以使用 DEL 命令删除 HyperLogLog 中所有元素。例如,清空 key 为 hll 的 HyperLogLog:

```

DEL hll

```

“相关推荐”对你有帮助么?

非常没帮助

没帮助

一般

有帮助

非常有帮助

提交

来自宇宙的曹先生

CSDN认证博客专家

CSDN认证企业博客

码龄3年

南昌大学

273

原创

6179

周排名

6595

总排名

13万+

访问

等级

4787

积分

1542

粉丝

2042

获赞

4

评论

1707

收藏

私信

关注

热门文章

虚拟机(VM)与JVM

2185

详细介绍设计模式七大原则

2047

使用Spring Boot操作Redis、ES、MongoDB举例

1823

MVCC及其原理

1581

Gateway:微服务架构中的关键组件

1564

分类专栏

设计模式

19篇

文献翻译

2篇

研究生阶段周报

3篇

Java复习

5篇

旅游项目

14篇

最新评论

深入理解设计模式:建造者模式

爱编程的饭团:

优质好文,博主的文章细节很到位,兼顾实用性和可操作性,感谢博主的分享,期待博主持续带来更多好文

事务的传播行为

CSDN-Ada助手:

恭喜你,获得了 2023 博客之星评选的入围资格,请看这个帖子 (https://bbs.csdn.net/topics/617754224?utm_source=blogger_star_comment)。 请在这里提供反馈: https://blogdev.blog.csdn.net/article/details/129986459?utm_source=blogger_star_comment。

哪些数据适合放入缓存?

白话机器学习:

博主的文章细节很到位,兼顾实用性和可操作性,感谢博主的分享,期待博主持续带来更多好文

线程池七大参数解释

CSDN-Ada助手:

恭喜您撰写了第20篇博客!标题“线程池七大参数解释”非常吸引人,让我对您的博客产生了浓厚的兴趣。您对线程池的七大参数进行解释,无疑为读者提供了宝贵的知识点。不仅如此,您的文章标题也很吸引人,能够引起读者的注意。

在下一步的创作中,我建议您可以进一步扩展您的内容,例如深入探讨每个参数的具体应用场景,以及如何优化线程池的性能等。此外,您还可以考虑添加一些实际案例或者代码示例,以帮助读者更好地理解和应用您所讲解的内容。

总之,我对您的持续创作充满期待,希望您能够继续分享更多有价值的知识给读者。谦虚地说,您的博客已经给我带来了很多收获,期待您在未来的创作中再接再厉!

您愿意向朋友推荐“博客详情页”吗?

强烈不推荐

不推荐

一般般

推荐

强烈推荐

提交

最新文章

Redis中的整数集合数据结构为什么不支持降级操作?

MurmurHash2算法

将List转换为数组或者将数组转换为List,如果改变了原始值,转换后的数据会发生改变吗?

2024

03月

12篇

02月

26篇

01月

50篇

2023年167篇

2022年19篇

目录

目录

分类专栏

设计模式

19篇

文献翻译

2篇

研究生阶段周报

3篇

Java复习

5篇

旅游项目

14篇

目录

评论

被折叠的  条评论

为什么被折叠?

到【灌水乐园】发言

查看更多评论

添加红包

祝福语

请填写红包祝福语或标题

红包数量

红包个数最小为10个

红包总金额

红包金额最低5元

余额支付

当前余额3.43元

前往充值 >

需支付:10.00元

取消

确定

下一步

知道了

成就一亿技术人!

领取后你会自动成为博主和红包主的粉丝

规则

hope_wisdom 发出的红包

实付元

使用余额支付

点击重新获取

扫码支付

钱包余额

0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

嵌入式系统应用开发学习笔记(五):HLS_嵌入式系统开发中hls是指-CSDN博客

>

嵌入式系统应用开发学习笔记(五):HLS_嵌入式系统开发中hls是指-CSDN博客

嵌入式系统应用开发学习笔记(五):HLS

最新推荐文章于 2022-05-19 12:09:06 发布

VIP文章

且听风吟☆

最新推荐文章于 2022-05-19 12:09:06 发布

阅读量446

收藏

点赞数

分类专栏:

嵌入式系统应用开发

文章标签:

fpga

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/TopLuther/article/details/117374312

版权

一、 HLS是什么?与VHDL/Verilog有什么关系?

HLS全称高层次综合(high level synthesis),采用C/C++等高级语言描述功能,可以降低FPGA代码的开发时间和验证时间

VHDL/Verilog两种语言都是用于数字电子系统设计的硬件描述语言,而且都已经是 IEEE 的标准

HLS与VHDL/Verilog一样都是FPGA的开发语言

二、 HLS有哪些关键技术问题?目前存在什么技术局限性?

关键技术问题: 与Verilog相比,能做到的优化十分有限

优化手段

HDL

HLS

BRAM分割

单次遍历

无缝数据交换

×

背靠背迭代

×

浮点除法优化

×

浮点累加优化

×

动态参数:HLS禁止动态分配指针,因为硬件设计通常具有多个存储空间,并且该工具必须知道函数打算访问哪个存储空间,以便可以相应地放置电线连接。动态传递函数参数的一种方法是通过静态定义所有可能的函数调用并对每个参数进行硬编码来引入间接层,然后使用条件语句动态选择正确的调用。

最低0.47元/天 解锁文章

优惠劵

且听风吟☆

关注

关注

0

点赞

0

收藏

觉得还不错?

一键收藏

知道了

0

评论

嵌入式系统应用开发学习笔记(五):HLS

一、什么是HLS?与VHDL/Verilog有什么关系?HLS被称为高级合成。采用C/C++等高级语言对功能进行描述,减少了FPGA代码的开发时间和验证时间VHDL和Verilog都是数字电子系统设计的硬件描述语言,是IEEE标准与VHDL/Verilog一样,HLS是FPGA的开发语言二、 HLS的关键技术问题是什么?技术限制是什么?2.1 HLS关键技术问题与Verilog相比,优化效果非常有限动态参数:HLS禁止指针的动态分配,因为硬件设计通常有多个内存空间,并且工具必须知道函数要访问哪

复制链接

扫一扫

专栏目录

参与评论

您还未登录,请先

登录

后发表或查看评论

XAPP599-Vivado HLS的浮点设计

jerwey的博客

08-04

1788

使用Vivado HLS工具进行浮点设计的基础知识

Although these cores can be generated for custom precision floating-point types by the CORE Generator tool, only the single- and double-precision versions of the cores described by the IEEE-754 Standard are generated by the Vivado

hls fifo_【FCCM2020】HLS 高手对比 Verilog 高手,到底输哪了?

weixin_39602569的博客

11-21

328

防秒退提醒:如果你不懂 HLS 但是懂 FPGA,也可以读一下 Verilog 层面的优化手段。高级综合(high-level synthesis,简称 HLS)能自动把 C/C++ 之类的高级语言转化成 Verilog/VHDL 之类的底层硬件描述语言(RTL),生成定制硬件在 FPGA 上跑实现加速。这使得不懂硬件的软件工程师也可以拥有玩转硬件的能力。HLS 的存在已有多年,它的优点,极短的开...

HLS学习笔记

Something

05-10

420

最终转换的是HDL,也就是说HDL干不了的HLS肯定干不了,HDL能干的HLS也不一定能成。HLS与HDL相比,使用C/C++来设计电路,并可以自动添加流水、循环(部分)展开等操作。

C仿真是验证的C文件的语法正确性?C综合的输出文件是HDL文件吗?然后后续步骤就是常规HDL设计流程了?

-C仿真是算法层面的验证, C test bench验证的是C设计的正确性而非和C设计文件一起转换成HDL和相应的tb文件之后再进行验证(这一过程也存在),这样使得验证速度提升,时序等正确性由HLS工具来保证。

dire

Vivado HLS问题记录一:c/rtl联合仿真

zgg12345654321的博客

05-03

2342

使用HLS进行C仿真和运行都没问题,但无法进行C/RTL联合仿真,报错如下

我也不知道为什么出现这种错,但是如果顶层函数是int类型的就可以顺利进行c/rtl联合仿真,按理说hls支持浮点运算。

最后解决方法,重新安装vivado(采用的是viavado hl system edition安装,第一次是vivado hl webpack安装),安装好后在vivado中进行了如下操作

最后重新建立...

iOS流媒体开发之三:HLS直播(M3U8)回看和下载功能的实现.zip

最新发布

02-08

IOS开发

hls4ml:使用HLS在FPGA中进行机器学习

04-02

我们使用高级综合语言(HLS)创建机器学习算法的固件实现。 我们将传统的开源机器学习软件包模型转换为HLS,可以根据您的用例进行配置! 联络人: 文档和教程有关更多信息,请访问以下网页: : 可以在找到有关如何...

嵌入式边缘检测算法的HLS加速实现

01-12

针对嵌入式图像处理中边缘检测算法要求越来越高的实时性,通过Xilinx的Vivado HLS工具加速实现嵌入式图像处理中的边缘检测算法。基于Vivado HLS工具,选用ALINX-7020作为开发平台对Sobel边缘检测进行加速。根据...

hls_cnn_samples:HLS CNN

05-13

HLS CNN样本 在Vivado HLS中实现CNN的一些示例代码。 用法 运行软件 先决条件 CMake gcc 建造 mkdir -p build && cd build && cmake .. && make 跑步 # under directory build/ # run LeNet ./lenet 运行Vivado ...

FPGA并行编程-以HLS实现数字信号处理为例

11-09

本书将着重介绍高层次综合(HLS) 算法的使用并以此完成一些比较具体、细分的FPGA应用。我们的 目的是让读者认识到用HLS创造并优化硬件设计的好处。当然,FPGA的并行编程肯定是有别于在多核处理 器、GPU上实行的并行编程,但是一些最关键的概念是相似的,例如,设计者必须充分理解内存层级和带 宽、空间局部性与时间局部性、并行结构和计算与存储之间的取舍与平衡。

HLS介绍 - 02 - HLS原理与软件编译器的区别

Vuko_Coding Zone

08-25

1048

写在前面

通过将FPGA作为执行结构,Vivado HLS能够优化代码的吞吐量、功耗和延迟。本文参考UG998的第四章,主要介绍Vivado HLS的工作原理及其与传统软件编译器的区别。

运算

运算是指计算结果值所涉及的应用程序的算术和逻辑组件。此定义排除比较语句,因为它们是在条件语句中处理的。

在处理操作时,Vivado HLS与其他编译器的主要区别在于对设计的限制。对于处理器编译器,固定的处理体系结构意味着用户只能通过限制操作依赖性和操纵内存布局来影响性能,从而最大限度地提高缓存性能。

相比之下,Viv

嵌入式系统应用开发学习笔记(四)

TopLuther的博客

04-24

179

文章目录Verilog编程巩固练习在线编程网站的基本使用方法一、门电路1.1 与门1.2 或非门1.3 异或非门二、组合逻辑电路2.1 半加器2.2 全加器2.3 选择器三、时序逻辑电路3.1 D触发器3.2 8位D触发器3.3 可复位的D触发器3.4 锁存器安装 Robei

Verilog编程巩固练习

在线编程网站的基本使用方法

Verilog在线编程网站

变成界面就在主页面上

在下方有各种元件的选择框,点进去就可以看到对应的用法和编程框,写入程序提交后,网站会自动将你的答案与正确答案对比纠错,给出波形

嵌入式软件开发学习笔记-总序

luwb520的博客

06-21

143

这是我第一次写博客,在以前的很多学习过程中,依靠着别人的博客学到了很多东西。吃水不忘挖水人,近期自己准备开始嵌入式软件的学习,所以将会不定期更新学习笔记,大家一起学习。

文件的基本操作

KuQI滴滴滴的博客

09-23

311

文件的创建和打开

f=open('date.txt','w') #创建一个文件并且打开

f=open('date.txt') #打开date.txt文件

文件的写入和读取

f.write('hello wolrd!') #写入信息

f.read() #读取信息存到暂存区

文件的关闭

f.close() #关闭当前文件

...

hls心得(2)-关于hls处理浮点数运算

kebu12345678的博客

03-15

3409

浮点运算存在运算精度问题,统一个算法,放在不同的实现环境,结果会不同。

在C++代码中, hls有两个头文件

这个放在testbench中

 这个文件放在 要编译成STL代码的cpp文件中

这样可以保证在simulation的时候, hls_math中的数学函数返回的结果与STL后返回的精度一致。 

否者,如果都用cmath,会导致C-simulati

嵌入式系统学习笔记

xiaochu233的博客

05-19

1438

嵌入式系统学习笔记

HLS设计方法论 - 优化硬件函数方法论及流水操作的优化策略

Vuko_Coding Zone

09-12

1323

写在前面

本文参考赛灵思的官方手册UG1270,主要介绍了硬件函数的设计方法论和操作的优化策略,以帮助我们更好的进行HLS的开发设计。

优化硬件函数概述

Vivado HLS 的默认行为是以顺序方式执行函数和循环,以便硬件准确反映 C/C++ 代码。 优化指令可用于增强硬件函数的性能,允许流水线操作,从而显着提高函数的性能。 本文概述了优化设计以获得高性能的一般方法。

在尝试使用 Vivado HLS 优化设计时,有许多可能的目标。 该方法假设您希望创建具有尽可能最高性能的设计,在每个时钟周期处理一个新输入

HLS优化总结

tiaozhanzhe1900的博客

11-29

6547

文章目录1 任意精度数据类型2 unroll&amp;amp;amp;amp;amp;pipeline2.1 循环平坦化2.2 循环展开2.3 循环合并3 dataflow4 数组4.1 array partition4.2 array reshape

部分内容总结自https://blog.csdn.net/weixin_41967965/article/details/82080230

http://www.openhw.o...

error: HLS/hls.h: No such file or directory

06-03

这个错误提示意味着编译器无法找到名为"hls.h"的头文件。这可能是因为您的编译器未正确安装或配置HLS库所致。您可以尝试以下步骤来解决这个问题: 1. 确认您已经正确安装了HLS库,并且该库的路径已经添加到编译器的...

“相关推荐”对你有帮助么?

非常没帮助

没帮助

一般

有帮助

非常有帮助

提交

且听风吟☆

CSDN认证博客专家

CSDN认证企业博客

码龄5年

暂无认证

52

原创

9万+

周排名

136万+

总排名

3万+

访问

等级

691

积分

94

粉丝

20

获赞

18

评论

127

收藏

私信

关注

热门文章

关于浏览器以及各种内置浏览器造成黑屏的解决办法

11240

计算机网络实验报告(三):Cisco Packet Tracer 实验

4939

嵌入式系统应用开发实验(一)

1378

Windows API程序入门学习(1)

1252

计算机网络实验报告(二):Wireshark 实验

1083

分类专栏

嵌入式培训

16篇

人工智能与机器学习

6篇

嵌入式系统应用开发

8篇

网络通信编程

6篇

嵌入式系统基础学习日记

9篇

计算机网络实验

3篇

最新评论

关于浏览器以及各种内置浏览器造成黑屏的解决办法

m0_75142671:

遇到相同的问题很久了,希望楼主的办法可以解决

C语言学习笔记w2d5

风继续吹TT:

都是博主辛苦创作,我来支持一下,期待大佬回访!

C语言学习笔记w2d5

风信子的猫Redamancy:

好文,已收藏,大佬分析的很到位,明白了很多,大赞!( ̄ˇ ̄),大佬有兴趣也可以看下我的博客哈

C语言学习笔记w2d5

Dream丶Killer:

好文,受益匪浅,支持,欢迎回访!

C语言学习笔记w2d4

宇宙爆肝锦标赛冠军:

受益匪浅!

您愿意向朋友推荐“博客详情页”吗?

强烈不推荐

不推荐

一般般

推荐

强烈推荐

提交

最新文章

ARM:day5

ARM:day4

Linux基础与C高级w3d4:linux的文件管理(续)、了解shell编程

2021年34篇

2020年19篇

目录

目录

分类专栏

嵌入式培训

16篇

人工智能与机器学习

6篇

嵌入式系统应用开发

8篇

网络通信编程

6篇

嵌入式系统基础学习日记

9篇

计算机网络实验

3篇

目录

评论

被折叠的  条评论

为什么被折叠?

到【灌水乐园】发言

查看更多评论

添加红包

祝福语

请填写红包祝福语或标题

红包数量

红包个数最小为10个

红包总金额

红包金额最低5元

余额支付

当前余额3.43元

前往充值 >

需支付:10.00元

取消

确定

下一步

知道了

成就一亿技术人!

领取后你会自动成为博主和红包主的粉丝

规则

hope_wisdom 发出的红包

实付元

使用余额支付

点击重新获取

扫码支付

钱包余额

0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

【2022年的STM32】 03-HAL与LL(Low-layer APIs)与MCU驱动概述-CSDN博客

>

【2022年的STM32】 03-HAL与LL(Low-layer APIs)与MCU驱动概述-CSDN博客

【2022年的STM32】 03-HAL与LL(Low-layer APIs)与MCU驱动概述

最新推荐文章于 2023-04-21 12:37:01 发布

VIP文章

mcucpp

最新推荐文章于 2023-04-21 12:37:01 发布

阅读量461

收藏

点赞数

分类专栏:

STM32

文章标签:

stm32

单片机

mcu

嵌入式

arm

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/Kinetis_Linux/article/details/122503058

版权

概述

区别与以前的固件库(标准固件库SPL),ST推出了新的固件库HAL与LL,当然,LL只适合部分外设,是层次更低的API,可以缩减代码大小,本文对硬件抽象层HAL和LL层做概要分析,后续将在各个章节中使用HAL构建上层的基础支持层或者应用。 ST官方链接:ST HAL UserManul

HAL与LL

– The STM32Cube HAL, STM32 abstraction layer embedded software ensuring maximized portability across the STM32 portfolio. HAL APIs are available for all peripherals. – Low-layer APIs (LL) offering a fast light-weight expert-oriented layer which is closer to the hardware than the HAL. LL APIs are available only for a set of peripherals.

__STATIC_INLINE uint32_t LL_GPIO_ReadInputPort(GPIO_TypeDe

最低0.47元/天 解锁文章

优惠劵

mcucpp

关注

关注

0

点赞

0

收藏

觉得还不错?

一键收藏

知道了

0

评论

【2022年的STM32】 03-HAL与LL(Low-layer APIs)与MCU驱动概述

比较好的固件库应该是分为2层,最底层是硬件层(SDK HW),其根据功能对寄存器的单一位域进行访问,较高层是驱动功能层(SDK DRV),其对外设功能进行实现,比如串口初始化,那么它调用HW硬件层的配置波特率、配置启停位、配置校验接口,最后对UART模块使能。

复制链接

扫一扫

专栏目录

IIC_LCD1602 STM32-HAL驱动程序-硬件IIC

11-11

STM32F103C8T6驱动LCD(带PCF8574转接板,IIC驱动),利用CUBEMAX生成,HAL库硬件IIC.

STM32 HAL固件库详解

Dir_x的博客

02-04

2047

STM32 HAL固件库详解、CMSIS库、HAL库文件详解、HAL库文件之间的关系

参与评论

您还未登录,请先

登录

后发表或查看评论

HAL 固件库结构整理

刘星燎

05-27

1291

提示:本文以STM32Cube_FW_H7_V1.5.0为例

部分解释

CMSIS:微控制器软件接口标准 (CMSIS) 是 Cortex-M 处理器系列的与供应商无关的硬件抽象层。CMSIS 可实现与处理器和外设之间的一致且简单的软件接口,从而简化软件的重用,缩短微控制器开发人员新手的学习过程,并缩短新设备的上市时间。

CMSIS-CORE:提供与 Cortex-M0、Cortex-M3、Cortex-M4、SC000 和 SC300 处理器与外围寄存器之间的接口

CMSIS-DSP:是一个经过深入优化

百问7天物联网智能家居实战训练营提高班 —— STM32F103基于HAL库的移植与基本配置

addison666的博客

07-23

878

移植中断源文件和头文件在软件包中找到project文件夹打开文件夹中的STM3210C_EVAL/Templates/Src将其中的stm32f1xx_it.c以及STM3210C_EVAL/Templates/Inc中的stm32f1xx_it.h文件复制到项目文件夹中的core文件夹中。移植启动文件CMSIS/Device/ST/STM32F1XX/Source/Templates/arm中的startup_stm32f103xb.s复制到项目文件夹中的project文件夹中。...

ST的HAL库运行过程中卡死问题

qq_33432189的博客

09-04

2336

ST的HAL库运行过程中卡死问题

近期在使用HAL库的串口功能时,在多字节发送接收出现了运行中突然卡死问题,

具体表现为串口不再接收,发送功能正常。

问题原因在于使用HAL_UART_Transmit_IT发送多字节数据时,如果被串口中断打断发送,

huart->RxState状态在HAL_UART_Transmit_IT中被设置成HAL_UART_STATE_BUSY_TX,

这个时候进入中断以后HAL_UART_Receive_IT查看该标志不为HAL_UART_STATE_RE

STM32标准库和HAL库的区别

TYINY的博客

10-26

7586

大体看了下,标准库移植起来没有HAL库方便。

用HAL库移植起来不需要改动,怪不得ST推HAL库

https://blog.csdn.net/ice_masters/article/details/105644704

https://blog.csdn.net/bornpride/article/details/94601754

怪不得这里说没有可移植性,我最开始看到这句的时候很吃惊,标准库怎么没有可移植性了。

...

STM32cube-HAL工程建立踩坑.pptx

06-29

cube生成的文件库如何调用C文件和H文件。生成的工程模板如何进行第一次的配置即开发使用。文件的存放地址与文件的加载关系。

STM32F1开发指南-HAL库版本_V1.0.pdf

04-18

正点原子 STM32F1开发指南-HAL库版本

RT-Thread+stm32f10x-HAL 使用ADC设备程序

08-11

嵌入式操作系统RT-Thread的ADC设备使用示例(使用的bsp为rt-thread-v4.0.1版本的stm32f10x-HAL)。 注:rt-thread-v4.0.1\bsp\stm32\stm32f103-fire-arbitrary 可以通过env工具运行menuconfig直接使用ADC设备,使用...

stm32-hal-4G模块程序

最新发布

06-03

stm32的4G模块代码,实现的功能有: 获取模块4G信号、ICCID号;获取模块IME,并组建登录包身份信息 配置DTU联网参数:工作模式、IP地址、端口号,心跳包

航芯通用MCU技术常见问题 | F0专题

weixin_43362622的博客

08-23

1109

航芯通用MCU技术常见问题

MCU BSP Driver分层设计

qq_44829055(金丝草)的博客

01-19

556

如果你关注类似于此设计,你的代码编辑水平已经可以了。

stm32开发层次介绍(转载)

木牛的博客

04-21

510

除了这些盗版IDE集成开发环境,其实我们还有另外一种选择,使用Linux开发,在Linux开发是基于GCC的,使用GCC就意味着要写Makefile,这一连串的新问题又来了,为了解决盗版问题,ST和NXP都推出了基于Eclipse+GCC的集成开发环境。LL库(Low Layer)是 ST 最近新增的库,与 HAL 库捆绑发布,文档也是和 HAL 库文档在一起的,比如:在STM32F3x 的 HAL 库说明文档中,ST 新增了LL库这一章节,但是在 F2x 的HAL文档中就没有。

GD32F4上使用HAL库实现USB虚拟串口通讯

yy123xiang的专栏

09-01

5979

一直使用STM32F4搭配HAL库做项目,最近需要将软件移植至国产的GD32F4上,但兆易仅提供了标准库,并未像ST那样提供HAL库,但二者USB库又互不兼容。最后,为了原软件不做大的修改,故决定使用ST的HAL库+兆易的USB库来实现。

首先是USB库文件的准备,兆易官网可下载GD32F4xx_Firmware_Library,将Firmware\GD32F4xx_usb_library目录下的相关文件拷贝至工程目录中,并添加至工程中,文件目录结构如下图所示:

另外,在Examples\USB\USB

STM32外设驱动库分析及如何实现

李肖遥的专栏

03-03

293

关注、星标公众号,直达精彩内容来源:https://blog.csdn.net/twx11213030422一、如何控制单片机?单片机的内存映射图解析这里以STM32F429芯片为例,讲解下单片机芯片内存映射图。从此图中可以看到芯片的外设被分配了512M的空间,然而真正的外设其实没有使用到512M的内存空间。然后我们操作外设时,只需要操作它对应的内存地址即可。更加详细的外设内存地址,可以参...

MCU程序框架

maggieyu12的博客

01-21

1799

参加工作3年以来辗转于不同MCU编写的过程中,每使用一款新的MCU总要重新开发,但是不同MCU的差异性导致程序编写进度缓慢,程序混乱难以维护。因此我意识到程序框架的重要性,它能帮助你将程序从不同硬件环境中抽离出来,独立运行,从而使得程序一致性强易于维护和移植。在此我发表一下自己的薄见,如有设计不合理之处,请各位大佬指出。

首先应先依据功能划分将不同功能封装到不同的C文...

STM32CubeF4 FreeRTOS Examples don't work correctly with HAL_GetTick

weixin_33955681的博客

07-29

298

because the SysTick ISR has been assigned to the FreeRTOS xPortSysTickHandler() function without regard to HAL operations.

I think this can be corrected by calling HAL_IncTick() before calling xPortS...

STM32【H7】理论——综述、HAL库简述

Adam's Rib for Antennnin

06-30

8064

文章目录1. STM32H7芯片简介1.1 STM32H7与STM32F1、F4系列芯片的区别1.2 硬件框图1.3 STM32H7各型号对比1.4 总线框图和时钟1.5 AXI总线1.5.1 AXI总线简介1.5.2 AXI总线优先级编程1.6 总线互联1.7 FLASH1.8 RAM1.9 电源系统1.9.1 系统上电启动1.9.2 低功耗模式1.9.3 电源去耦电容的选择1.10 复位系统1.10.1 硬件复位1.10.2 软件复位1.11 RCC 时钟系统1.11.1 STM32H7 RCC时钟简介

037_AUTOSAR学习笔记_MCU驱动

小灰笔记

12-15

2215

         主要功能:MCU初始化、断电功能、复位、MCAL其他模块的特殊需求。

         几种模式:

         外部时钟的PLL时钟模式、晶振的PLL时钟模式、晶振参考下的PLL关闭模式。

内部RC电路主要是用来监控晶振以及锁相环质量的。

DMA应该有多种优先级,这个优先级跟中断是否是一个概念?

从下文看,有一个嵌套的说法,看起来有可能跟中断时...

stm32f1-hal-and-lowlayer-drivers-中文翻译

05-16

另一方面,STM32F1-Low-Layer驱动程序提供了一种更加低级别的编程接口,允许开发人员与STM32F1芯片的寄存器和底层硬件直接进行交互。选择使用较低层驱动程序的原因可能是为了提高代码执行速度,或者需要使用没有在...

“相关推荐”对你有帮助么?

非常没帮助

没帮助

一般

有帮助

非常有帮助

提交

mcucpp

CSDN认证博客专家

CSDN认证企业博客

码龄9年

暂无认证

29

原创

13万+

周排名

92万+

总排名

4万+

访问

等级

374

积分

47

粉丝

17

获赞

7

评论

193

收藏

私信

关注

热门文章

NXP S32K1 DMA模块

4128

SPI时钟极性、相位设置

3397

【使用C++开发MCU】05-CAN实例之NXP S32K1 FlexCAN模块

3263

NXP S32K1 SPI模块

2747

NXP S32K1 Timer之LPIT概述

2594

分类专栏

瑞萨RH850

STM32

5篇

S32K1

22篇

C++开发MCU

10篇

UML应用与嵌入式开发

1篇

AUTOSAR标准

2篇

最新评论

NXP S32K1 DMA模块

wsssdz:

你这不就是翻译了一下数据手册

NXP S32K1 DMA模块

遇见嵌入式:

副循环是指每次传输多少数据,主循环是指重复多少遍。比如串口发送100字节,你可以设置主循环100,副循环1或者主循环1,副循环100。主副循环对应到dma的控制会有一点差别。

NXP S32K1 DMA模块

北明有yu:

TCD的主循环和副循环主要作用是?

NXP S32K1 DMA模块Driver使用

老马他舅舅:

博主有没有DMA采集ADC的例程呢

【使用C++开发MCU】00-专栏前言

ilikerome:

关注更新

您愿意向朋友推荐“博客详情页”吗?

强烈不推荐

不推荐

一般般

推荐

强烈推荐

提交

最新文章

【2022年的STM32】 05-引脚中断实例

【2022年的STM32】 04-GPIO特性、使用及与NXP GPIO比较

【2022年的STM32】 02-STM32总览

2022年20篇

2021年11篇

目录

目录

分类专栏

瑞萨RH850

STM32

5篇

S32K1

22篇

C++开发MCU

10篇

UML应用与嵌入式开发

1篇

AUTOSAR标准

2篇

目录

评论

被折叠的  条评论

为什么被折叠?

到【灌水乐园】发言

查看更多评论

添加红包

祝福语

请填写红包祝福语或标题

红包数量

红包个数最小为10个

红包总金额

红包金额最低5元

余额支付

当前余额3.43元

前往充值 >

需支付:10.00元

取消

确定

下一步

知道了

成就一亿技术人!

领取后你会自动成为博主和红包主的粉丝

规则

hope_wisdom 发出的红包

实付元

使用余额支付

点击重新获取

扫码支付

钱包余额

0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

HLL (HyperLogLog) | StarRocks

HLL (HyperLogLog) | StarRocks

跳到主要内容StarRocksDocsLatest-3.2Latest-3.23.13.0Stable-2.52.32.22.1中文English中文Release Notes社区群StarRocks中文社区论坛技术支持渠道Privacy policy搜索StarRocks产品简介快速开始技术支持部署 StarRocks表设计导入数据导出数据查询数据湖查询加速外部系统集成管理手册参考手册数据类型数值类型字符串类型日期类型半结构化类型其他类型BITMAPHLL (HyperLogLog)关键字SQL 参考函数参考数据类型数值类型字符串类型日期类型半结构化类型其他类型BITMAPHLL (HyperLogLog)Information SchemaHTTP APIAUTO_INCREMENT生成列预准备语句系统变量用户自定义变量系统限制AWS IAM 策略错误码常见问题解答性能测试开发指南参考手册数据类型其他类型HLL (HyperLogLog)版本:Latest-3.2本页总览HLL (HyperLogLog)

描述​

HyperLogLog 类型,用于近似去重。详细的使用方法请参考 使用 HyperLogLog 实现近似去重。

HLL 是基于 HyperLogLog 算法的工程实现,用于保存 HyperLogLog 计算过程的中间结果,HLL 类型的列只能作为表的 value 列类型,通过聚合来不断的减少数据量,以此来实现加快查询的目的。基于 HLL 到的是一个估算结果,误差大概在 1% 左右。

HLL 列是通过其它列或者导入数据里面的数据生成的,导入的时候通过 HLL_HASH 函数来指定数据中哪一列用于生成 HLL 列它常用于替代 count distinct,通过结合 rollup 在业务上用于快速计算 UV。

HLL类型使用的存储空间取决于 HLL 中插入的 hash 值的去重数量。分为三种情况考虑:

HLL 为空: 未插入任何值,HLL 的存储代价最小,占用 80 字节。

HLL 中不重复 hash 值的个数 ≤160

最大代价为: 80 + 160 * 8 = 1360 字节。

HLL 中不重复 hash 值的个数 >160

存储代价固定为: 80 + 16 * 1024 = 16464 字节。

实际使用中,数据量和数据分布会影响查询的内存使用量和近似结果的准确性,因此需要考虑:

数据量: 因为 HLL 是统计估计算法,数据量大,误差就小;数据量较小时,误差就大。

数据分布:数据量很大时,group by 维度列基数很高的情况下,计算会使用更多内存, 不推荐使用HLL去重。 no-group-by 去重、group by 维度列基数很低的情况下,推荐使用 HLL 去重。

当用户查询粒度较粗时:最好使用聚合表或者物化视图对数据进行预聚合对聚合列进行 rollup 降低数据规模。

相关函数​

HLL_UNION_AGG(hll):此函数为聚合函数,用于计算满足条件的所有数据的基数估算。此函数还可用于分析函数,只支持默认窗口,不支持 window 子句。

HLL_RAW_AGG(hll):此函数为聚合函数,用于聚合 hll 类型字段,并且返回的还是 hll 类型。

HLL_CARDINALITY(hll):此函数用于计算单条 hll 列的基数估算。

HLL_HASH(column_name):生成 HLL 列类型,用于 insert 或导入的时候,导入的使用见相关说明。

HLL_EMPTY():生成空 HLL 列,用于 insert 或导入的时候补充默认值,导入的使用见相关说明。

示例​

创建含有 HLL 列的表​

create table test( dt date, id int, name char(10), province char(10), os char(1), set1 hll hll_union, set2 hll hll_union)distributed by hash(id);

通过 Stream load 导入数据​

a. 使用表中的列生成 HLL 列。curl --location-trusted -uname:password -T data -H "label:load_1" \ -H "columns:dt, id, name, province, os, set1=hll_hash(id), set2=hll_hash(name)"http://host/api/test_db/test/_stream_loadb. 使用数据中的某一列生成 HLL 列curl --location-trusted -uname:password -T data -H "label:load_1" \ -H "columns:dt, id, name, province, sex, cuid, os, set1=hll_hash(cuid), set2=hll_hash(os)"http://host/api/test_db/test/_stream_load

通过 HLL 聚合数据​

聚合数据有 3 种常用方式:

-- 基于第一个示例创建的 HLL 表创建一个 rollup,让 UV 列产生聚合。alter table test add rollup test_rollup(dt, set1);-- 创建另外一张专门计算 UV 的表,然后 INSERT 数据。create table test_uv( dt date, id int, uv_set hll hll_union)distributed by hash(id);insert into test_uv select dt, id, set1 from test;-- 创建另外一张专门计算 UV 的表,然后 INSERT 并通过 hll_hash 根据 test 其它非HLL 列生成 HLL。create table test_uv( dt date, id int, id_set hll hll_union)distributed by hash(id);insert into test_uv select dt, id, hll_hash(id) from test;

查询 HLL 列​

查询 HLL 列不允许直接查询它的原始值,可以通过配套的函数进行查询。

-- 求总 UV。select HLL_UNION_AGG(uv_set) from test_uv;-- 求每一天的 UV。select dt, HLL_CARDINALITY(uv_set) from test_uv;-- 求 test 表中 set1 的聚合值。select dt, HLL_CARDINALITY(uv)from ( select dt, HLL_RAW_AGG(set1) as uv from test group by dt) tmp;select dt, HLL_UNION_AGG(set1) as uv from test group by dt;文档是否有帮助?提交反馈编辑此页上一页BITMAP下一页关键字描述相关函数示例创建含有 HLL 列的表通过 Stream load 导入数据通过 HLL 聚合数据查询 HLL 列StarRocks.ioPrivacy policyCopyright © 2023 StarRocks, Inc. Built with Docusaurus.