Spark源码分析-topN源码

Spark实现TopN 大数据实验教学系统练习 Spark实现TopN因为Spark RDD是分区并行计算的,因此要排序的话,需要指定一个分区。   使用sortByKey算子,按key排序,然后再使用take算子,取前几个元素,就得到了 Top N 的结果。掌握抓取文本中最大的前几位数字。   掌握排序算子的使用。1、使用Spark RDD实现 Top N。    假设我们有以下输入文件top.txt: 编写RDD代码,获取值最大的三个数(Top 3)。    2、使用Spark RDD实现分组 Top N。    假设我们有以 阅读详情
  1. 介绍

TopN算子是取RDD的前N个元素。取TopN元素,我们就一定要对其进行严格排序吗?非也,也正是如此,加大了此算子的效率。

  1. 源码理解

  

  def top(num: Int): JList[T] = {
  val comp = com.google.common.collect.Ordering.natural().asInstanceOf[Comparator[T]]
  top(num, comp)
}

我们需要传进去一个整形的参数,然后后面声明了一个默认的比较器comp,又调用了两个参数的top函数,然后可以看到又调用了RDD的takeOrdered函数:

def takeOrdered(num: Int)(implicit ord: Ordering[T]): Array[T] = withScope {
  if (num == 0) {
    Array.empty
  } else {
    val mapRDDs = mapPartitions { items =>
      // Priority keeps the largest elements, so let's reverse the ordering.
      val queue = new BoundedPriorityQueue[T](num)(ord.reverse)
      queue ++= collectionUtils.takeOrdered(items, num)(ord)
      Iterator.single(queue)
    }
    if (mapRDDs.partitions.length == 0) {
      Array.empty
    } else {
      mapRDDs.reduce { (queue1, queue2) =>
        queue1 ++= queue2
        queue1
      }.toArray.sorted(ord)
    }
  }
}

    RDD的takeOrdered函数里面使用的是mapPartitions算子,从这里就可以知道他首先需要在每个partition中求top信息,然后就在对所有的partition信息进行统一的排序。

    重点看collectionUtils.takeOrdered方法,里面调用了一个ordering.leastOf(input.asJava, num)方法,重点看这个方法里面的内容:

public <E extends T> List<E> leastOf(Iterator<E> elements, int k) {
  checkNotNull(elements);
  checkArgument(k >= 0, "k (%s) must be nonnegative", k);

  if (k == 0 || !elements.hasNext()) {
    return ImmutableList.of();
  } else if (k >= Integer.MAX_VALUE / 2) {
    // k is really large; just do a straightforward sorted-copy-and-sublist
    ArrayList<E> list = Lists.newArrayList(elements);
    Collections.sort(list, this);
    if (list.size() > k) {
      list.subList(k, list.size()).clear();
    }
    list.trimToSize();
    return Collections.unmodifiableList(list);
  }

  /*
   * Our goal is an O(n) algorithm using only one pass and O(k) additional
   * memory.
   *
   * We use the following algorithm: maintain a buffer of size 2*k. Every time
   * the buffer gets full, find the median and partition around it, keeping
   * only the lowest k elements.  This requires n/k find-median-and-partition
   * steps, each of which take O(k) time with a traditional quickselect.
   *
   * After sorting the output, the whole algorithm is O(n + k log k). It
   * degrades gracefully for worst-case input (descending order), performs
   * competitively or wins outright for randomly ordered input, and doesn't
   * require the whole collection to fit into memory.
   */
  int bufferCap = k * 2;
  @SuppressWarnings("unchecked") // we'll only put E's in
  E[] buffer = (E[]) new Object[bufferCap];
  E threshold = elements.next();
  buffer[0] = threshold;
  int bufferSize = 1;
  // threshold is the kth smallest element seen so far.  Once bufferSize >= k,
  // anything larger than threshold can be ignored immediately.

  while (bufferSize < k && elements.hasNext()) {
    E e = elements.next();
    buffer[bufferSize++] = e;
    threshold = max(threshold, e);
  }

  while (elements.hasNext()) {
    E e = elements.next();
    if (compare(e, threshold) >= 0) {
      continue;
    }

    buffer[bufferSize++] = e;
    if (bufferSize == bufferCap) {
      // We apply the quickselect algorithm to partition about the median,
      // and then ignore the last k elements.
      int left = 0;
      int right = bufferCap - 1;

      int minThresholdPosition = 0;
      // The leftmost position at which the greatest of the k lower elements
      // -- the new value of threshold -- might be found.

      while (left < right) {
        int pivotIndex = (left + right + 1) >>> 1;
        int pivotNewIndex = partition(buffer, left, right, pivotIndex);
        if (pivotNewIndex > k) {
          right = pivotNewIndex - 1;
        } else if (pivotNewIndex < k) {
          left = Math.max(pivotNewIndex, left + 1);
          minThresholdPosition = pivotNewIndex;
        } else {
          break;
        }
      }
      bufferSize = k;

      threshold = buffer[minThresholdPosition];
      for (int i = minThresholdPosition + 1; i < bufferSize; i++) {
        threshold = max(threshold, buffer[i]);
      }
    }
  }

  Arrays.sort(buffer, 0, bufferSize, this);

  bufferSize = Math.min(bufferSize, k);
  return Collections.unmodifiableList(
      Arrays.asList(ObjectArrays.arraysCopyOf(buffer, bufferSize)));
  // We can't use ImmutableList; we have to be null-friendly!
}

    从最上面开始,如果k过大,超过了Int类型最大数的一半,那么久直接用Collections.sort进行排序,底层的排序代码如下:

default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}

    把原始的List放到一个数组中,然后用Arrays.sort进行排序,这个排序默认用的是Timsort方法排序(优化的归并排序-归并和插入排序集合),然后将排好序的数组重新放到list中,这也好解释,为什么上面有list.subList(k, list.size()).clear();代码,就是为了把所有多余的数据去掉。上面的是特殊情况,一般情况下没人取那么大的top值。

    下面接着看正常情况下的代码:

    首先声明了一个长度为k*2大小的数组buffer:

int bufferCap = k * 2;

E[] buffer = (E[]) new Object[bufferCap];

为什么是k*2的长度,是为了quickselect算法的实现:

在leastOf函数的第一个While循环中,直接将前K个元素复制到buffer数组中,并且记录前K个元素中的最大值,然后开始下面的while循环。总体思路如下:

开始遍历后面的元素,判断当前元素与前K元素中最大值比较,如果大于等于最大值,然后往buffer中放入值,直到长度为k*2的数组buffer元素填充满,然后开始取前K大值的操作:

int pivotIndex = (left + right + 1) >>> 1;

左右指针相加,再加一,然后无符号右移1位,取到buffer中位数的指针。然后后面的具体过程就是将所有的buffer中的元素前k大的元素移动到0到k-1之中,这个过程不需要排序,只需要移动动前k大的元素,并且需要记录此时前K大元素中最大值,然后再按照上面的过程再次将buffer填充满,再次移动找到前K大的值,直至遍历完所有的数。我们可以得到buffer中前K个元素就是该分区中最大的前K个值。

Arrays.sort(buffer, 0, bufferSize, this);

bufferSize = Math.min(bufferSize, k);
return Collections.unmodifiableList(
    Arrays.asList(ObjectArrays.arraysCopyOf(buffer, bufferSize)));

最后将数组排序,返回最前K大的值。

TOPMIN库:嵌入式系统中高效追踪N个最小值的轻量级方案 在嵌入式数据流处理中,动态维护前N个极值(如最小值)是环境监测、工业告警等场景的基础需求。其核心原理在于避免全量排序与动态内存分配,转而采用固定大小升序数组+插入排序策略,实现O(1)空间复杂度与O(N)确定性插入时间。该技术显著提升实时系统的可预测性与可靠性,特别适用于Arduino、STM32等资源受限MCU平台。结合标签机制(如时间戳、传感器ID),可支撑温度极值记录、电压跌落分析等典型应用。TOPMIN正是面向此类工程痛点设计的轻量级C++模板库。 阅读详情

相关推荐

Spark map与mapPartitions算子源码级深度解析

实现特征fT => U。

Aaron_PHPer的博客 972

机器具备认知能力的“神”操作!

简介: 目前认知概念图谱的构建还在不断完善中,有学者认为,概念是人脑对客观事物的本质反映,是思维活动的结果和产物,是思维活动借以开展的基本单元。建立概念的分类体系,为数以亿计的事物构建认知概念图谱,是人类在让机器具备认知能力的征程中所迈出的非常重要的一步。 背景 概念是人类认知从具体进入抽象的第一步,也是人类认知世界的基石。有学者认为,概念是人脑对客观事物的本质反映,是思维活动的结果和产物,是...

阿里云开发者 5677

ToPMine:我托管的是Ahmed El-Kishky最初在github上提供的文件,只做了最小的更改

托品 我托管的是Ahmed El-Kishky最初在github上提供的文件,只做了最小的更改。 从El-Kishky Illimine版权 伊利诺伊大学香槟分校,2014年 illimine.cs.illinois.edu 附加版权 该软件包包含以下论文中使用的源代码和数据集: @inproceedings {conf / vldb / ElkishkySWVH13,作者= {Ahmed El-Kishky和Yanglei Song和Chi Chi和Clare R. Voss和Hanjiawei},标题= {来自文本语料库的可扩展主题短语挖掘},书名= {VLDB},年= {2015},} 如果您使用此包装中的任何物品,请引用以上文章作为参考。 代码说明 将输入语料库放置在rawFiles中,并将每个文档排成一行。在run.sh(linux)/win_run.bat(对于Window

15_VLDB_Scalable topical phrase mining from text

While most topic modeling algorithms model text corpora with unigrams, human interpretation often relies on inherent grouping of terms into phrases. As such, we consider the problem of discovering topical phrases of mixed lengths. Existing work either perf

Fitz的博客 614

Spark的TopN示例

Spark中实现MapReduce的TopN算法,分为唯一键TopN算法,非唯一键TopN算法和Group内的TopN算法

使用堆实现Top K算法(JS实现)

主要介绍了使用堆实现Top K算法,即JS实现,文中详细介绍了Top K算法,感兴趣的小伙伴们可以参考一下

[Spark的TopN算法实现]

一、TopN算法    MapRedce中的TopN算法是一个经典的算法,由于每个map都只是实现了本地的TopN算法,而假设map有M个,在归约的阶段只有M x N次运算,这个结果是可以接受的并不会造成性能瓶颈。  MapRedce中的TopN算法在map阶段将使用TreeMap来实现排序,以到达可伸缩的目的。      本文将介绍三种TopN的算法:       1.唯一键的TopN算法,就是...

程序车轮 2万+

Kriging代理模型理论相关推导

代理模型,Kriging,加点准则

森屹的博客 1万+

spark 设置主类_Spark资源调度、任务调度源码及流程图

1. Spark-Submit提交参数Options: Master: MASTER_URL, 可以是spark://host:port, mesos://host:port, yarn, yarn-cluster,yarn-client, localdeploy-mode:DEPLOY_MODE, Driver程序运行的地方,client或者cluster,默认是client。Class:CLA...

weixin_39603823的博客 296

别再为数据发愁:5分钟理解事件相机原理,并自己动手生成事件图像

本文深入浅出地解析了事件相机的工作原理,并提供了从静态图像生成事件数据的实用方法。通过Python代码示例,读者可以快速掌握如何模拟事件相机的输出,应用于机器人导航、自动驾驶等领域,解决数据获取难题。

weixin_31458015的博客 300

行为验证码 AJ-Captcha 使用文档

AJ-Captcha验证码系统集成指南:需添加captcha-spring-boot-starter和Redis依赖,配置Redis连接。核心实现包括Redis缓存服务类CaptchaCacheRedisImpl(含设置、查询、删除验证码等方法)、SPI配置文件及Spring Boot配置类(配置验证码属性和Redis模板)。系统支持验证码生成、校验、防刷等安全功能,适用于Web应用安全验证场景。

千层冷面的博客 2115

Spark核心编程:高级编程之topn

案例:对每个班级内的学生成绩,取出前3名1>文件部分数据

江南水景 553

同花顺Supermind量化交易 风险控制建模--海龟交易法则

A.市场与标的海龟交易法则应用于流动性高的市场中,选择交易量较大的标的进行交易。本文以沪深300指数ETF为标的,构建海龟交易法则。B、仓位:仓位是海龟交易系统最核心的部分,通过ATR真实波幅指标来管理仓位。第一步:计算True Range,简称TR。TR = Max ( H−L , H−P , P−L ) ,其中H为当日日内最高价,L为当日日内最低价,P为前一日收盘价。第二步:计算ATRATR = mean ( TR , 20 ) , 即计算过去20天的TR的平均值,ATR是TR的移动平均值。

qq_22808537的博客 1497

在 Spark RDD 中,sortBy 和 top 算子的各自适用场景

用于对整个数据集进行分区排序或者全局排序。在 Spark RDD 中,个最大/最小的元素。

z1941563559的博客 814

YOLOv9改进策略【注意力机制篇】| 添加SE、CBAM、ECA、CA、Swin Transformer等注意力和多头注意力机制

这篇文章带来一个经典注意力模块的汇总,虽然有些模块已经发布很久了,但后续的注意力模块也都是在此基础之上进行改进的,对于初学者来说还是有必要去学习了解一下,以加深对模块,模型的理解。

Limiiiing的博客 2892

华为OD机试新系统真题-魔法咒语组合(Java/Py/C++/Go/C/Js)

华为OD机试 新系统真题【魔法咒语组合】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【魔法咒语组合】多语言题解

qq_45776114的博客 668

spark总结01

目录 第一部分:SaprkCore部分 Spark简介 1、什么是RDD? RDD的5大特性。 2、怎么理解partition,如何合理的设置partition的数量。 3、RDD或者partition里面存储数据吗?怎么理解内存计算。 4、Spark中的hello world (word count) 5、Spark架构原理 6、创建初始的RDD 7、RDD算子操作 8、RD...

yangshaojun1992的博客 827

YOLOv8 Neck改进:BiFPN双向特征金字塔网络解析与实践

本文详细介绍了如何在YOLOv8中集成BiFPN双向特征金字塔网络,包括原理解析、代码实现、配置文件修改和性能分析。BiFPN通过双向特征融合和加权特征机制,显著提升了多尺度目标检测的性能。希望通过本文的讲解,大家能够深入理解BiFPN的工作原理,并成功将其应用到自己的目标检测项目中。如果你有任何问题或建议,欢迎在评论区留言讨论!在下一篇文章中,我们将探讨YOLOv8的其他改进技术,包括注意力机制、新的损失函数等,敬请期待!

QQ_778132974的博客 991

spark学习

Scala基础语法大全总结 https://blog.youkuaiyun.com/qq_37142346/article/details/80977744 Spark基本工作原理与RDD https://blog.youkuaiyun.com/kaizuidebanli/article/details/83829304 Spark wordcount基本原理深度剖析 https://blog.youkuaiyun.com/yilu...

weixin_43212037的博客 162

跨越边界:用MessagePipe实现无缝的进程间通信

MessagePipe是一个用于.NET平台的消息传递库,它专注于简化异步消息处理和进程间通信。通过提供一系列易于使用的API,开发者可以快速搭建起稳定可靠的通信管道,支持多种传输协议(如TCP、Unix域套接字等),并内置了序列化/反序列化功能,使得跨语言和跨平台的数据交换变得轻而易举。为了方便序列化和反序列化操作,我们需要为每种消息定义对应的类或结构体,并标记属性。这样可以确保消息在发送方和接收方之间正确无误地传递。[Key0set;[Key1set;

墨夶的博客 816

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Flink项目实战系列(Spark项目实战系列)

本人的知识星球内发布了大量的Flink和Spark的项目实战,文章的目录如下: 初识Flink Flink读取Kafka源码解读 Flink的状态后端 Flink的runtime Flink系列之数据流编程模型 Flink系列之checkpoint Flink系列之savepoint Flink系列之checkpoint和savepoint的区别 Flink系列之部署Standalone模式 ...

JasonLee实时计算 5800

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Spark——底层操作RDD,基于内存处理数据的计算引擎

Spark 第一章 是什么一 介绍简介特点二 Spark与MapReduce的区别三 Spark运行模式四 Spark CoreSpark RDDRDD LineageSpark任务执行原理Spark代码流程Spark 中的算子Transformations转换算子Action行动算子控制算子 第一章 是什么 一 介绍 简介 Apache Spark是一个快速的通用集群计算引擎。它提供Java,...

时间静止 4555

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

3.算子+PV&UV+submit提交参数+资源调度和任务调度源码分析+二次排序+分组topN+SparkShell...

1.补充算子 transformations Ø mapPartitionWithIndex 类似于mapPartitions,除此之外还会携带分区的索引值。 Ø repartition 增加或减少分区。会产生shuffle。(多个分区分到一个分区不会产生shuffle) 多用于增多分区. 底层调用的是coalesce Ø coalesc...

weixin_30315905的博客 141

Spark Core与Hadoop生态复习

Spark Core作用:内核,提供RDD、任务调度、内存管理、容错机制等。核心特性弹性分布式数据集(RDD)延迟计算(Lazy Evaluation)容错机制(DAG、血缘关系)内存计算口诀:RDD弹性,懒加载,血缘容错,快如闪电。Driver:作业提交、任务调度Executor:具体计算、内存缓存:资源分配Worker(Standalone模式):运行Executor进程口诀:Driver调度,Executor计算,Manager分配。Spark核心。

IT技术学习与工作笔记分享 1183

支持spark_大牛用10小时就把Spark讲完了,总计2.2G,6大技术文档

前言Apache Spark是一个开源集群运算框架,相对于Hadoop的MapReduce会在运行完工作后将中介数据存放到磁盘中,Spark使用了存储器内运算技术,能在数据尚未写入硬盘时即在存储器内分析运算。Spark 在存储器内运行程序的运算速度能做到比 Hadoop MapReduce 的运算速度快上 100 倍,即便是运行程序于硬盘时,Spark 也能快上 10 倍速度。Spark 允许用户...

weixin_29000417的博客 163

spark集群搭建_大牛用10小时就把Spark讲完了,总计2.2G,6大技术文档

前言Apache Spark是一个开源集群运算框架,相对于Hadoop的MapReduce会在运行完工作后将中介数据存放到磁盘中,Spark使用了存储器内运算技术,能在数据尚未写入硬盘时即在存储器内分析运算。Spark 在存储器内运行程序的运算速度能做到比 Hadoop MapReduce 的运算速度快上 100 倍,即便是运行程序于硬盘时,Spark 也能快上 10 倍速度。Spark 允许用户...

weixin_39862847的博客 105
上一篇: Spark数据倾斜的原因以及解决方案
下一篇: Spark源码分析-SortByKey源码
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值