当前位置: 首页 > 后端技术 > Python

推荐系统大规模特征工程和FEDB的基于Spark的LLVM优化

时间:2023-03-25 23:34:56 Python

今天和大家分享第四范式在推荐系统大规模特征工程和基于Spark的LLVM优化的实践,主要包括以下四个主题。大规模推荐系统特征工程介绍SparkSQL和FESQL架构设计LLVM-basedSpark性能优化推荐系统与Spark优化应用程序中的应用程序。可以说,大部分互联网公司和传统公司都可以通过推荐系统来提升商业价值。我们将通用推荐系统架构分层。离线层主要负责对存储在HDFS中的海量数据进行预处理和特征提取,然后使用主流的机器学习训练框架进行模型训练并导出模型。该模型可以提供给在线服务使用。流层(Streamlayer),也称为近线层,是介于离线和在线之间的中间层。可以使用Flink等流式计算框架进行近实时的特征计算和生成,结果存储在NoSQL或关系型数据库中。用于在线服务。在线层(Onlinelayer)包括与用户交互的UI和在线服务。实时提取流式特征并使用离线模型进行估计,实现推荐系统、估计结构和用户反馈的在线召回和推荐功能。也可以通过事件调度程序写入将耗尽计算和离线Hadoop存储的队列。本次分享将重点关注离线层的优化。在一个大规模的推荐系统中,离线存储的数据可能达到PB级别。常用的数据处理有ETL(Extract、Transform、Load)和FE(Featureextraction),编程工具主要有SQL和Python。为了能够处理大规模数据,一般会使用Hadoop、Spark、Flink等分布式计算框架。其中Spark是业界使用最广泛的,因为它同时支持SQL和Python接口。SparkSQL和FESQL架构设计Spark刚刚发布了3.0版本,在性能和可用性上都有了很大的提升。与HadoopMapReduce相比,性能加速100倍以上,可处理PB级数据量,支持水平扩展。分布式计算和自动故障转移,支持简单易用的SQL、Python、R和流式编程(SparkStreaming)、机器学习(MLlib)和图计算(GraphX)接口,对于推荐系统,内置的推荐算法模型也可以开箱即用。业界有很多场景使用Spark作为推荐系统的离线数据处理框架,例如使用Spark加载分布式数据集,使用SparkUDF和SQL进行数据预处理和特征选择,使用MLlib训练召回和排序模型.但是,Spark不支持在线部分。主要原因是Spark不支持longrunningservice,driver-executor架构只适合离线批处理计算。Spark3.0推出了Hydrogen,支持部分预运行任务,但只适用于离线计算或模型。计算阶段对实时性要求较高,但在线服务支持不佳。SparkRDD编程接口也适用于迭代计算。我们得出结论,Spark的优势在于可以批量处理大规模数据,支持标准的SQL语法。缺点是不支持线上估价服务,无法保证线下线上服务。一致性,对AI场景的特征计算没有太多优化。第四范式自研的FESQL服务,基于SparkSQL,为AI场景特征提取计算提供性能优化,从根本上解决离线在线一致性问题。传统的AI落地场景是先在离线环境下通过机器学习训练框架导出AI模型文件,然后让业务开发者搭建线上服务。由于离线使用SQL和Python进行数据预处理和特征提取,Online需要开发配套的在线处理框架。两种不同的计算系统容易出现离线和在线功能不一致的情况。甚至离线建模也可能使用遍历功能使在线部分无法实现。FESQL解决方案使用统一的SQL语言。除了标准的SQL支持,还扩展了AI场景的计算语法和UDF定义。离线和在线使用同一套高性能LLVMJIT代码生成,保证离线和在线都执行相同的计算逻辑,从而保证机器学习中离线和在线特征的一致性。为了支持SparkSQL无法支持的在线功能,FESQL的在线部分实现了自研的高性能全内存时序数据库。与Redis、VoltDB等其他常见的key-value内存数据库相比,在时序特征的存储上具有更好的读写性能。并且压缩能力有了很大的提升,比OpenTSDB等传统时序数据库更能满足在线业务的超低延迟需求。离线部分仍然依赖于Spark的分布式任务调度功能,但是使用了更高效的原生执行引擎来进行SQL的解析和执行。C++实现的LLVMJIT代码生成技术可以使用更多的内在函数为现代CPU实现向量。优化等指令集优化,甚至FPGA、GPU等特殊硬件加速。通过同一个SQL执行引擎的优化,不仅提高了离线和在线的执行效率,而且在功能上保证了离线建模的特征提取方案无需额外的开发和对比工作就可以迁移到在线服务中。FESQL的性能是和memsql比较的,也是全内存商业产品。在机器学习的时序特征抽取场景中,同样的SQL相比memsql性能也有很大的提升。基于LLVM的Spark性能优化从Spark2.0开始,Catalyst和Tungsten项目用于优化Spark和SQL任务的性能。Catalyst通过对SQL语法的词法分析和句法分析,生成一个未解析的抽象语法树数据结构,并对抽象语法树进行了数十次优化。最终生成的物理计划可以比普通的SQL解析执行得更快。几十次。Tungsten项目利用Javaunsafe接口实现了内部数据结构的堆外管理,大大降低了JVMGC的开销,并且可以针对多个物理节点和多个表达式实现全阶段codegen,直接生成Java字节码进行编译优化使用Janino内存编译器。生成的代码避免了过多的虚函数调用,提高了CPU缓存的命中率。性能比传统火山模型解释执行快数倍,非常接近高级程序员手写的Java代码。表现。那么Spark的Catalyst和Tungsten是否足够完美?我们认为这还不够。首先,Spark是基于Scala和Java实现的。甚至PySpark也是通过sockets连接到JVM来调用Java函数的。所以,所有的代码都是在JVM上执行的,不可避免地要接受JVM和GC的开销。而且,随着CPU硬件和指令集的更新,通过JVM使用新的硬件特性依然困难重重,更不用说日益流行的FPGA和GPU了。对于高性能执行引擎,可以使用较低级别的C或C++实现。更好的性能改进代码。对于并行数据计算任务,可以通过使用循环展开等优化方法将性能提高一倍。对于连续的内存数据结构,可以做更多的矢量化优化和GPU上数千个计算核心的并行优化。这些目前在最新的Spark3.0开源版本中还是不支持的。另外,在机器学习场景中,经常会使用SQL的窗口函数来计算时序特征。这部分函数对应Spark的物理节点WindowExec,但并没有实现整个stagecodegen。也就是说,在做多表达式窗口计算时不能使用Tungsten的优化,通过解释和执行来计算每个特征,这样的性能甚至比用户自己编写的Java程序代码慢得多。为了解决Spark的性能问题,我们基于LLVM并兼容SparkSQL接口,实现了Spark的原生执行引擎。相对于Spark基于JVM可以生成逻辑节点,生成Java字节码,运行在物理机上,FESQL执行引擎还会解析SQL生成逻辑计划,然后通过JIT直接生成平台相关的机器码执行技术。从架构上看,JVM虚拟机层的开销相比Spark减少了,性能也会有很大的提升。LLVM是目前非常流行的编译系统工具链,其项目包括非常有名的Clang、LLDB等。TensorFlow在机器学习领域主要推广的MLIR和TVM,使用了LLVM技术,可以理解为生成编译器的工具,目前流行的编程语言如Ada、C、C++、D、Delphi、Fortran、Haskell、Julia、Objective-C、Rust、Swift等都提供了基于LLVM的编译器。JIT对应的是AOT的概念。AOT(Ahead-Of-Time)表示编译在程序运行之前执行。也就是说,我们经常写的C、Java代码,都是先编译成二进制或者字节码,然后再运行。这属于AOT编译。JIT(Just-In-Time)意思是在运行时编译和优化。现在Python、PHP等很多解释型语言都使用了JIT技术。对于运行频率非常高的热代码,使用JIT技术将其编译成平台优化的原生二进制文件,这种动态生成和编译代码的技术也称为JIT编译。LLVM提供了一个高质量、模块化的编译链接工具链,可以轻松实现AOT编译器,也可以集成到C++项目中实现自定义函数JIT。以下是实现简单添加功能的示例。与直接使用C编写函数实现相比,JIT需要在代码中定义函数头、函数参数、返回值等数据结构,最后由LLVMJIT模块生成平台相关的符号表和可执行文件格式.由于LLVM内置了大量的编译优化pass,所以自己实现的JIT编译器并不比GCC或者Clang差多少。JIT可以用来生成各种UDF(User-Definedfunctions)和UDAF(User-DefinedAggregationFunctions),LLVM支持多种后端。除了常见的x86、ARM等架构,还可以使用PTX后端生成运行在GPU上的CUDA代码。LLVM还提供底层内在函数接口,以便程序可以使用现代CPU指令集。性能媲美手写C甚至手写汇编。在2020Spark+AISummit上,Databrick不仅发布了Spark3.0,还提到了内部闭源项目Photon,作为Spark的原生执行引擎,可以加速SparkSQL的执行效率。Photon也是用C++实现的。从Databrick的实验数据可以看出,用C++实现的字符串处理等表达式的性能可以比用Java实现提高数倍,并且有更多的向量化指令集支持。整体设计与FESQL非常相似,但作为闭源项目,Photon目前只能在Databrick商业平台上使用。目前还在试验阶段,需要联系客服手动开通。由于没有更多的实现细节发布,所以不确定Photon是否基于LLVMJIT实现,暂时没有官方介绍对PTX或CUDA的支持。在FESQL提供的原生执行引擎上,也应用了很多节点优化和表达式优化技术。比如在Project节点中,使用SimpleProject可以优化未使用的列数据,引入节点运行数量和节点间数据传输量,并通过window节点的wholestagecodegen,可以直接与Project合并一个节点,一次迭代就可以得到所有需要的结果。在表达式优化方面,主流与Limit、Where、Constant折叠、Filter、Cast、Upper、Lower化简的融合,都可以通过optimizationpass进行优化,生成最简洁的表达式计算,从而大大减少CPU执行指令,以及相关的SQL优化我就不一一赘述了,只有经过逻辑节点优化、表达式优化、指令集优化、代码生成,才有可能达到接近顶级程序员手写代码的性能.在机器学习常用的时间序列特征抽取测试场景中,相同的SQL语句和测试数据基于相同版本的Spark调度引擎,使用FESQL的原生执行引擎在单窗口下性能提升近一倍.在window场景下,由于CPU计算更加密集,性能可以提升近6倍,多线程下结果也差不多。从结果来看,使用LLVMJIT的性能提升非常明显。使用相同的代码和SQL,不修改任何一行代码,只要更换SPARK_HOME下的执行引擎实现,就可以实现近6倍甚至更大的性能提升。我们从生成的计算图和火焰图中找到了性能提升的原因。首先我们在SparkUI上可以看到,SparkSQL中的window节点并没有实现整个stagecodegen,所以这部分是Scala代码的解释和执行,而SparkSQL的物理计划很长,检查和生成每个节点之间的不安全行都有一定的开销。与只有两个节点的FESQL相比,LLVMJIT的二进制代码读取数据后直接执行,大大减少了节点间的开销。从火焰图分析,最底层是Spark调度器的runTask函数。SparkSQL使用滑动窗口计算聚合特征时,样本数和耗时比较长,而FESQL是原生执行的。编译了基本的min、max、sum和avg。服务器优化后CPU执行时间更短。左边虽然有unsaferow编解码时间,但是占的并不多,整体时间比SparkSQL要少很多。FESQL是目前少有的比开源Spark3.0快数倍的原生执行引擎。它可以支持标准SQL并集成到Spark中。与只能在Databrick内部使用的Photon不同,我们将在未来发布集成的LLVMJIT优化引擎。LLVM-enabledSparkDistribution,无需修改任何一行代码,只要指定SPARK_HOME,即可获得极大的性能加速,同时兼容现有的Spark应用。更多FESQL用例请关注Github项目https://github.com/4paradigm/...。推荐系统和Spark优化总结最后,我们总结了我们在推荐系统和Spark优化方面的工作。首先,大规模的推荐系统必须依赖于能够处理大数据计算的框架,例如Spark、Flink、ES(ElasticSearch)和FESQL。Spark是目前最流行的大数据离线处理框架,但目前只适用于离线批处理,无法支持在线。FESQL是我们自主研发的SQL执行引擎。通过集成内部时序数据库,实现在线一键SQL,保证离线在线一致性。在内部,LLVMJIT可以优化SQL执行性能,比开源版本Spark3.0性能提升数倍。更多FESQL用例请关注Github项目https://github.com/4paradigm/...。