Bitmap篇

 

前一篇中介绍了使用API做Distinct Count,但是计算精确结果的API都较慢,那有没有能更快的优化解决方案呢?

1. Bitmap介绍

《编程珠玑》上是这样介绍bitmap的:

Bitmap是一个十分有用的数据结构。所谓的Bitmap就是用一个bit位来标记某个元素对应的Value,而Key即是该元素。由于采用了Bit为单位来存储数据,因此在内存占用方面,可以大大节省。

简而言之——用一个bit(0或1)表示某元素是否出现过,其在bitmap的位置对应于其index。《编程珠玑》给出了一个用bitmap做排序的例子:

/* Copyright (C) 1999 Lucent Technologies */
/* From 'Programming Pearls' by Jon Bentley */
/* bitsort.c -- bitmap sort from Column 1
* Sort distinct integers in the range [0..N-1]
*/
#include <stdio.h> #define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N / BITSPERWORD]; void set(int i) { a[i >> SHIFT] |= (1 << (i & MASK)); } void clr(int i) { a[i >> SHIFT] &= ~(1 << (i & MASK)); } int test(int i) { return a[i >> SHIFT] & (1 << (i & MASK)); } int main() {
int i;
for (i = 0; i < N; i++)
clr(i);
/* Replace above 2 lines with below 3 for word-parallel init
int top = 1 + N/BITSPERWORD;
for (i = 0; i < top; i++)
a[i] = 0;
*/
while (scanf("%d", &i) != EOF)
set(i);
for (i = 0; i < N; i++)
if (test(i))
printf("%d\n", i);
return 0;
}

上面代码中,用int的数组存储bitmap,对于每一个待排序的int数,其对应的index为其int值。

2. Distinct Count优化

index生成

为了使用bitmap做Distinct Count,首先需得到每个用户(uid)对应(在bitmap中)的index。有两种办法可以得到从1开始编号index表(与uid一一对应):

  • hash,但是要找到无碰撞且hash值均匀分布[1, +∞)区间的hash函数是非常困难的;
  • 维护一张uid与index之间的映射表,并增量更新

比较两种方法,第二种方法更为简单可行。

UV计算

在index生成完成后,RDD[(uid, V)]RDD[(uid, index)]join得到index化的RDD。bitmap的开源实现有EWAH,采用RLE(Run Length Encoding)压缩,很好地解决了存储空间的浪费。Distinct Count计算转变成了求bitmap中1的个数:

// distinct count for rdd(not pair) and the rdd must be sorted in each partition
def distinctCount(rdd: RDD[Int]): Int = {
val bitmap = rdd.aggregate[EWAHCompressedBitmap](new EWAHCompressedBitmap())(
(u: EWAHCompressedBitmap, v: Int) => {
u.set(v)
u
},
(u1: EWAHCompressedBitmap, u2: EWAHCompressedBitmap) => u1.or(u2)
)
bitmap.cardinality()
} // the tuple_2 is the index
def groupCount[K: ClassTag](rdd: RDD[(K, Int)]): RDD[(K, Int)] = {
val grouped: RDD[(K, EWAHCompressedBitmap)] = rdd.combineByKey[EWAHCompressedBitmap](
(v: Int) => EWAHCompressedBitmap.bitmapOf(v),
(c: EWAHCompressedBitmap, v: Int) => {
c.set(v)
c
},
(c1: EWAHCompressedBitmap, c2: EWAHCompressedBitmap) => c1.or(c2))
grouped.map(t => (t._1, t._2.cardinality()))
}

但是,在上述计算中,由于EWAHCompressedBitmap的set方法要求int值是升序的,也就是说RDD的每一个partition的index应是升序排列:

// sort pair RDD by value
def sortPairRDD[K](rdd: RDD[(K, Int)]): RDD[(K, Int)] = {
rdd.mapPartitions(iter => {
iter.toArray.sortWith((x, y) => x._2.compare(y._2) < 0).iterator
})
}

为了避免排序,可以为每一个uid生成一个bitmap,然后在Distinct Count时将bitmap进行or运算亦可:

rdd.reduceByKey(_ or _)
.mapValues(_._2.cardinality())

3. 参考资料

[1] 周海鹏, Bitmap的秘密.

Bitmap的更多相关文章

  1. [翻译]开发文档:android Bitmap的高效使用

    内容概述 本文内容来自开发文档"Traning > Displaying Bitmaps Efficiently",包括大尺寸Bitmap的高效加载,图片的异步加载和数据缓存 ...

  2. 【开源毕设】一款精美的家校互动APP分享——爱吖校推 [你关注的,我们才推](持续开源更新3)附高效动态压缩Bitmap

    一.写在前面 爱吖校推如同它的名字一样,是一款校园类信息推送交流平台,这么多的家校互动类软件,你选择了我,这是我的幸运.从第一次在博客园上写博客到现在,我一次一次地提高博文的质量和代码的可读性,都是为 ...

  3. Android Bitmap 和 ByteArray的互相转换

    Android Bitmap 和 ByteArray的互相转换 移动平台图像处理,需要将图像传给native处理,如何传递?将bitmap转换成一个 byte[] 方便传递也方便cpp代码直接处理图像 ...

  4. Android-Drawable、Bitmap、byte[]、资源文件相互转换

    我们在Android的开发中,经常可以遇到图片的处理,当中,有很多是 Bitmap.Drawable.byte[]和资源文件它们直接相互转换. 今天就此总结一下: 1.资源文件转为Drawable 2 ...

  5. bitmap对海量无重复的整数排序--转

    原文地址:http://blog.csdn.net/u013074465/article/details/46956295 现在有n个无重复的正整数(n 小于10的7次方),如果内存限制在1.5M以内 ...

  6. 基于位图(Bitmap、BitmapData)的图片处理方法(C#)

    目前操作位图的主流方法有三种: 1.基于Bitmap像素的处理方法,以GetPixel()和SetPixel()方法为主.方法调用简单,但是效率偏低. 2.基于内存的像素操作方法,以System.Ru ...

  7. android:布局、绘制、内存泄露、响应速度、listview和bitmap、线程优化以及一些优化的建议!

    1.布局优化 首先删除布局中无用的控件和层级,其次有选择地使用性能较低的viewgroup,比如布局中既可以使用RelativeLayout和LinearLayout,那我们就采用LinearLayo ...

  8. 获取View的截图-将View转换为Bitmap对象

    开发中,有时候需要获取View的截图来做动画来达到动画流程的目的 原理:将View的内容画到一个Bitmap画布上,然后取出 下面封装了一个从View生成Bitmap的工具类 /** * 将View转 ...

  9. bitmap解码

    #include <stdio.h> #include <stdlib.h> #include <string.h> #define BYTE unsigned c ...

  10. Bitmap转换成BitmapImage

    public BitmapImage BitmapToBitmapImage(System.Drawing.Bitmap bitmap) { MemoryStream ms = new MemoryS ...

随机推荐

  1. SpringMVC之Controller传递JSON数据到页面

    在Controller中,组装好JSON格式的数据,然后输入到页面,或者通过ajax请求在页面进行解析,都可以做到. 1.Controller /** * JSON DATA TO PAGE VEIW ...

  2. 【Demo 0001】Android 程序结构

    Android 学习步骤及内容:     1.  Android 程序结构(开发环境搭建,Android第一程序,程序启动过程以及工程介绍):     2.  Android 事件(通用使用规则,通用 ...

  3. Spark SQL Catalyst源代码分析之TreeNode Library

    /** Spark SQL源代码分析系列文章*/ 前几篇文章介绍了Spark SQL的Catalyst的核心执行流程.SqlParser,和Analyzer,本来打算直接写Optimizer的,可是发 ...

  4. Delphi事件的广播 good

    明天就是五一节了,辛苦了好几个月,借此机会应该尽情放松一番.可是想到Blog好久没有写文章,似乎缺些什么似的.这几个月来在项目中又增长了许多经验,学到许多实际应用的知识.不如把一些比较有用的记录下来, ...

  5. Lucene.Net 2.3.1开发介绍 —— 二、分词(二)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(二) 1.2.分词的过程 1.2.1.分词器工作的过程 内置的分词器效果都不好,那怎么办?只能自己写了!在写之前当然是要先看看内置的分词 ...

  6. ecshop 微信支付插件

    眼下已完毕支付測试,可以支付成功,支付逻辑自己实现.后台通知.发货通知.订单查询未測. 当中用到了redis 下载

  7. linux-sfdisk 使用方法

    功能说明:硬盘分区工具程序. 语 法:sfdisk [-?Tvx][-d <硬盘>][-g <硬盘>][-l <硬盘>][-s <分区>][-V < ...

  8. 并发编程实践五:ReentrantLock

    ReentrantLock是一个可重入的相互排斥锁,实现了接口Lock,和synchronized相比,它们提供了同样的功能.但ReentrantLock使用更灵活.功能更强大,也更复杂.这篇文章将为 ...

  9. 验证码 Captcha 之大插件

    验证码 Captcha 之大插件小用 不知何年何月才能完成OADemo啊,总之还是一步一步来吧,这段时间开始着手了,先做登陆.  前段时间研究了一下在CentOS7下安装Mysql和Memcached ...

  10. Python语言总结 4.2. 和字符串(str,unicode等)处理有关的函数

    4.2.7. 去除控制字符:removeCtlChr Python语言总结4.2. 和字符串(str,unicode等)处理有关的函数Sidebar     Prev | Up | Next4.2.7 ...