浅谈 Spark 的多语言支持
作者:鄭鍇,花名鐵杰,阿里巴巴高級技術(shù)專家,Apache Hadoop PMC,Apache Kerby 創(chuàng)立者。深耕分布式系統(tǒng)開發(fā)和開源大數(shù)據(jù)多年,先后專注在安全,存儲和計(jì)算領(lǐng)域。之前在 Intel,目前轉(zhuǎn)戰(zhàn)阿里云上,致力于提供更好用更有彈性的 Hadoop/Spark 大數(shù)據(jù)平臺。
Spark架構(gòu)和設(shè)計(jì)上的優(yōu)秀毋庸置疑,從一出道便搶了 Hadoop 的 C 位。在開源大數(shù)據(jù)的黃金十年一時(shí)風(fēng)頭無兩,在當(dāng)下人工智能時(shí)代仍然能夠與時(shí)俱進(jìn),通天之處不遑多言,美中不足之處也有不少。小的方面,比如調(diào)度模型跟 MapReduce 這種計(jì)算范式過于耦合,Spark 最近引入 Barrier 調(diào)度模式就是為了支持深度學(xué)習(xí)這種新的計(jì)算類型,所幸在于對框架的改動不會傷筋動骨;有些缺陷則不然,影響全局,調(diào)整起來絕非易事。今天我主要想談下 Spark 的框架語言實(shí)現(xiàn)和多語言支持---------有得,必有失。
Spark 核心和框架構(gòu)建在 Scala 上,對應(yīng)用開發(fā)者也是言必稱 Scala,直接秀起了代碼極簡的肌肉。在用戶接口上,從企業(yè)級應(yīng)用的首選 Java,到數(shù)據(jù)科學(xué)家的 Python 和 R,再到商業(yè)智能 BI 的 SQL,官方都一一支持,按說已經(jīng)很全面很完整了,各方神圣也都伺候到了,還有什么抱怨的,言何缺陷?首先,對 Python 的支持也就是 PySpark ,復(fù)雜且效率低下,有點(diǎn)像 hack,十分像早期 Spark 攻城略地的產(chǎn)出。其實(shí)對 R 的支持 SparkR 也如出一轍,我們討論清楚了 PySpark 也就明白了對其他語言的支持其實(shí)是類似的。從使用者的角度來看用 PySpark 寫程序的體驗(yàn)還是很不錯(cuò)的,跟 Scala 有點(diǎn)類似,簡潔而優(yōu)雅,就跟寫個(gè)單機(jī)程序一樣,不需要考慮分布式處理的復(fù)雜性。然而在這件漂亮的外衣下,用 PySpark 寫的 Spark 程序就是比 Scala 版本慢,還動不動 OOM 掉,為什么?我們來探一下究竟。
一般 Spark 程序首先給定輸入也就是要處理的數(shù)據(jù)集,然后表達(dá)好針對這個(gè)數(shù)據(jù)集的每行記錄要處理的邏輯和轉(zhuǎn)換規(guī)則,再將結(jié)果打印出來或是保存到文件。背后 Spark 框架進(jìn)行翻譯轉(zhuǎn)換,產(chǎn)出一個(gè)個(gè) RDD 和這些 RDD 之間的關(guān)系,每個(gè) RDD 表示對應(yīng)的數(shù)據(jù)集和在該數(shù)據(jù)集上要執(zhí)行的變換處理邏輯。這些 RDD 根據(jù)它們之間的依賴關(guān)系組成 DAG,對 DAG 翻譯轉(zhuǎn)換成 stages 和 tasks,這個(gè)過程在 driver 端由框架 Scala 代碼完成,包括對 stages 和 tasks 的調(diào)度執(zhí)行;RDD 的處理邏輯對應(yīng)用戶的 Scala 或 Python 代碼,因?yàn)橐植际讲l(fā)處理,主要在 executor 上執(zhí)行。因此,對于 Python 版本的 Spark 程序,在 driver 上和 executor 上都有要執(zhí)行的 Python 代碼,必然需要對應(yīng)的 Python 解釋器來執(zhí)行;然而 Spark 計(jì)算框架是 Scala/Java 代碼實(shí)現(xiàn)的,driver 和 executor 整體上得跑在 JVM 進(jìn)程里面。那么如何在 driver 端和 executor 上同時(shí)執(zhí)行代表用戶邏輯的 Python 代碼和核心引擎的 JVM 代碼,并在兩者之間進(jìn)行交互和協(xié)調(diào)呢?
PySpark 的做法是在 driver 端和 executor 上伴隨必需的 JVM 進(jìn)程,再 launch 起來單獨(dú)的 Python 解釋執(zhí)行進(jìn)程,然后通過 socket,文件和 pipeline 進(jìn)行交互和協(xié)作。這是個(gè)非常低效的做法,因?yàn)?Spark 程序通常不會應(yīng)用在一個(gè)普通場景里,而是要處理非常大的數(shù)據(jù)集。對于成千上萬行記錄的處理,都要在 executor 上通過跨進(jìn)程管道到 Python 進(jìn)程上來回一趟,末了在 driver 上,為了傳遞計(jì)算結(jié)果可能還要寫個(gè)磁盤文件才能轉(zhuǎn)給 Python 進(jìn)程,為此涉及到大量記錄數(shù)據(jù)在 Python 和 Java 之間要序列化和反序列化,效率可想而知。開啟新的 Python 進(jìn)程直接執(zhí)行用戶的代碼省事倒是省事,但是除了效率問題,還要考慮到進(jìn)程管理、內(nèi)存控制、容器環(huán)境和數(shù)據(jù)安全,這樣做的代價(jià)還是很大的,然而 Spark 卻不得不這樣做,主要是因?yàn)槭芟抻谒谡Z言支持上面缺乏長遠(yuǎn)的考慮和整體的設(shè)計(jì),這就是我想講的最重要的問題。
在語言支持上,從一開始 Spark 的關(guān)注點(diǎn)就是“糖衣”而非“炮彈”,在用戶體驗(yàn)上追求數(shù)據(jù)處理邏輯表達(dá)的簡潔有力,因此完勝當(dāng)時(shí)的各種計(jì)算框架而后一騎絕塵。記得在 Hadoop 如日中天的時(shí)候,筆者所在的 Intel 大數(shù)據(jù)團(tuán)隊(duì)正在玩 Hadoop 不亦樂乎,Spark 的作者 Matei 先生在遠(yuǎn)程會議上向我們演示當(dāng)時(shí)還十分稚嫩的 Spark,看到那個(gè)有名的 MapReduce Helloworld 例子 WordCount 程序只需要幾行 Scala 代碼搞定,真是駭人。Spark 能夠在短時(shí)間內(nèi)搶了 Hadoop 的風(fēng)頭,除了性能更快,毫無疑問這也是個(gè)關(guān)鍵因素。然而這種簡潔有力,在多大程度上要?dú)w功于 Scala,其實(shí)不太好講。最本質(zhì)的還是在于 RDD 本身的抽象,豐富的算子支持和巧妙的關(guān)系推導(dǎo),這部分可以歸為引擎層面,大體上用其他語言,比如更擅長做系統(tǒng)框架的,比如 C++,也能實(shí)現(xiàn)。至于用戶的程序是用 Scala 還是 Python,只要支持 closures,應(yīng)該都能行,簡潔程度上不會差別太大,也完全可以多支持一些語言,每個(gè)用戶具體用哪一個(gè)要看個(gè)人的偏好,沒有道理讓大家都去學(xué)一個(gè)新的語言.
可惜的是,Spark 引擎的實(shí)現(xiàn)應(yīng)該是受到了主要用戶接口語言 Scala 的影響,也采用了 Scala,部分采用 Java,但本質(zhì)上都是 JVM 語言,這樣做的好處是一鍋煮,省事??脊艑W(xué)家可以列出一大堆使用 Scala 的好處,比如代碼行數(shù)少,codegen 給力之類的,但是這些都是局部的,從整體上 Spark 作為一個(gè)統(tǒng)一的大數(shù)據(jù)處理平臺,其實(shí)更需要長遠(yuǎn)的通盤考慮。這個(gè)考慮就是在用戶接口上要能夠更快更好地支持更多的用戶語言,在計(jì)算引擎上能夠支持硬件層面的極致性能優(yōu)化,在計(jì)算場景上也能支持其他計(jì)算引擎的集成。
這個(gè)考慮簡單來說就是缺乏對框架層面的 C/C++ 支持。當(dāng)然這個(gè)支持很難在第一天就考慮進(jìn)來,畢竟關(guān)注點(diǎn)不在這兒,那個(gè)時(shí)候估計(jì) Matei 先生也想不到 Spark 會這么成功。然而框架層面缺乏對 C/C++ 的原生支持或者換句話說,框架核心沒有采用 C/C++ 這種 native 語言來開發(fā),其弊端和對 Spark 今后長遠(yuǎn)發(fā)展的影響毫無疑問顯而易見。C/C++ 作為系統(tǒng)語言,下可以跟硬件直接打交道,上可以直接對接各種開發(fā)語言,因此也不難支持各種計(jì)算處理庫和引擎。Spark 本身定位為一個(gè)大數(shù)據(jù)處理平臺,如果核心公共邏輯和操作交給 C/C++ 來實(shí)現(xiàn),那么首先支持 Python 這種用戶接口語言,直接利用 FFI 機(jī)制調(diào)用就是了,簡單且無損效率。主流的開發(fā)語言基本上都很好地支持 C/C++和Python ,像 Java,Go,Rust,Julia······。Spark 添加一個(gè)新的語言支持,就簡化為添加一個(gè)新的 binding,還好維護(hù),也不用擔(dān)心這個(gè)功能被閹割,那個(gè)支持還不全。對新的硬件做優(yōu)化也不成問題,比如從計(jì)算新貴 GPU 到 RDMA,再到可持久化大內(nèi)存 AEP,根本不會受限于 JVM 的限制,各大硬件廠商都能自己擼起袖子開搞,Spark 坐享其成就是?,F(xiàn)在對這些新硬件的支持為什么上不了,社區(qū)不接受?主要還是缺乏框架層面的支持,搞出來的都像是 hack,進(jìn)去也是麻煩??纯慈思?Tensorflow 就知道了,CPU,TPU,GPU 都能玩得轉(zhuǎn),否則跟不上來遲早面臨淘汰。
核心框架采用 Scala/Java 來實(shí)現(xiàn),還有一個(gè)重要的影響是數(shù)據(jù)集的內(nèi)存表示,早期 Spark 直接用 Java 對象來表示數(shù)據(jù)記錄,需要持久化或者網(wǎng)絡(luò)傳輸要序列化和反序列化,當(dāng)然在這一點(diǎn)上 Spark 后來很快意識到了問題,通過 offheap 和 Tunsten 項(xiàng)目馬上把問題解了,所幸還有招數(shù)。如果是 C/C++ 實(shí)現(xiàn)的,數(shù)據(jù)的內(nèi)存布局必然會采取類似 Arrow 的這種中性表示,方便各種用戶接口語言來訪問和操作,就像深度學(xué)習(xí)框架里面常見的 tensor,必須考慮高效傳遞。
最后一點(diǎn),采用 Scala/Java 來實(shí)現(xiàn)的 Spark 框架,雖然整合企業(yè)級應(yīng)用里面很常見的各種數(shù)據(jù)源得心應(yīng)手,但要整合集成更多的計(jì)算框架則就力不從心了。類似于對新硬件的支持,看一下 Yahoo 對 Caffe 和 Tensorflow 的支持就明白了,只能 hack 一下自己玩,不可能融入到 Spark 里面流行起來,要原生支持 deep learning,像 machine learning,可以,但要自己擼。Spark 自己花了很大力氣搞 mllib,Intel 搞 BigDL,都是沒辦法,因?yàn)槿狈诵目蚣艿慕o力支持,不能直接把這些領(lǐng)域里面現(xiàn)成的庫實(shí)現(xiàn)和計(jì)算引擎集成進(jìn)來。如果核心框架是是 C/C++ 實(shí)現(xiàn)的,集成 PyTorch 和 Tensorflow 能有多大的問題?無非是多掛一個(gè)動態(tài)模塊或搞一個(gè)插件的事情。
好吧,啰嗦到這兒,問題有解嗎?肯定有。Spark 搞定了用戶生態(tài),只要保持接口兼容不變,核心框架來個(gè)天翻地覆,改弦更張換個(gè)語言來實(shí)現(xiàn),未嘗不可,就看有沒有人愿意這么干。有沒有更取巧的辦法呢,不用聽起來這么嚇人?答案也是肯定的,筆者所在的團(tuán)隊(duì)就在干這樣的事情,阿里巴巴計(jì)算平臺,開源大數(shù)據(jù)部門 EMR,致力于在云上打造極致的彈性 Spark 計(jì)算體驗(yàn),做會在云上玩的 Spark,歡迎了解。
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的浅谈 Spark 的多语言支持的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 飞天2.0 | 万物智能的基础服务
- 下一篇: PostgreSQL 优化器代码概览