Pyodide:將Python科學計算全棧搬到瀏覽器

現在越來越多的軟件都Web化,瀏覽器化。雖然科學計算是一計算密集型的方向,對性能要求和實時性較高。但是數據計算方面也一直在做著這樣的探索和發展。

Jypyer notbook項目讓科學計算真正實現了Web化,而JS語言的發展和WebAssembly技術的興起,讓科學計算的瀏覽器化,在客戶端實現數據計算處理和可視化變成了可能。今天蟲蟲要給大家介紹就是一款來自Mozilla的實驗項目Pyodide,一款用於在瀏覽器中運行完整Python科學計算處理工具棧環境的軟件。

Pyodide:將Python科學計算全棧搬到瀏覽器

概述

Pyodide項目源於Mozilla的項目Iodide。 Iodide是基於Web技術的數據科學實驗和通信工具。它旨在在瀏覽器中進行數據計算。問題是通用的瀏覽器語言JS,沒有成熟的數據科學處理庫,也缺乏一些數值計算很有用功能和數據結構,比如運算符重載等。雖然完善JS語言本身是一條路,但是這條路太慢,所以有必要投機取巧的走一條捷徑出來。這就是Pyodide項目的所做的事情:通過將成熟活躍的Python科學計算工具棧引入瀏覽器來滿足數據科學計算的需要。Pyodide提供了一個完整的標準Python解釋器,它完全在瀏覽器中運行,可以完全訪問瀏覽器的Web API。下面以一個快速實例開始:

Pyodide:將Python科學計算全棧搬到瀏覽器

代碼顯示效果如下:

Pyodide:將Python科學計算全棧搬到瀏覽器

瞭解更多關於Pyodide可以做的事情的最好方法就是去嘗試吧!有一個演示筆記本(50MB下載),介紹了高級功能。本文的其餘部分將更深入地探討其工作原理。

技術架構

類似項目分析

Pydodie設計時候參考了很多現有項目,在實現Python到瀏覽器這"最後一公里"問題上,市面上已經有很多的項目,但是目前還沒有一個項目可以做到Python科學計算棧的轉化,該棧包括不限於Python類庫:

NumPy,Pandas,Scipy以及Matplotlib。

Transcrypt項目可以實現Python到JS的轉換。由於轉換步驟本身發生在Python中,所以你需要提前完成所有的轉換,或通過與服務器通信來完成這項工作。這並不能實現用戶在瀏覽器中編寫Python並實現即寫急用的目標。

BrythonSkulpt項目實現了將標準Python解釋器重寫為JS,可以直接在瀏覽器中運行Python代碼。但是它們都是對Python的全新實現,並且通過JS中引導,所以他們無法兼容C編寫的Python擴展,比如NumPy和Pandas。因此,無法引入數據科學工具。

PyPyJs項目是使用emscripten和JS對PyPy的瀏覽器實現。就像PyPy一樣它可以快速地運行Python代碼。同樣它也有於PyPy同樣的C擴展性能問題。

上面這很多項目都無法解決Python科學計算的問題,這促成了Pydodie的出現:構建一個儘可能基於Python的標準並能兼容實現大多數數據科學家已經使用的Python科學堆棧的工具庫的軟件。

Pydodie項目技術架構的關鍵技術就是新興的emscripten和WebAssembly。使用他們實現將C語言編寫的現有代碼庫移植到瀏覽器運行。Pyodide使用Python化的emscripten類庫cpython-emscripten為基礎。

Emscripten和WebAssembly

關於emscripten互聯網上有大量的介紹,在此不在贅述,在Pyodide項目中它的作用是:

實現從C/C++到WebAssembly的編譯器。

實現兼容性層,使瀏覽器變為一個本機計算環境。

WebAssembly是一種在現代Web瀏覽器中運行的新語言,是對JS腳本的補充。它以接近本機的性能運行在瀏覽器上,旨在作為C和C++等低級語言的編譯目標。

關於WebAssembly蟲蟲之前的文章中有介紹過,關注過蟲蟲的朋友可以在歷史文章中瀏覽學習。

我們知道,實現主流的Python解釋器是CPython是用C語言實現的,所以使用emscripten可以將其編譯為WebAssembly。

Pyodide組成和實現步驟

Pyodide主要架構組成如下:

將主流Python解釋器(CPython)的源代碼和科學計算包(NumPy等)將其打包在一起,使用emscripten的編譯器將它們編譯為WebAssembly。

但是如果只是在瀏覽器總執行這個WebAssembly還不夠,比如,無法在Web瀏覽器中執行文件操作(加載和保存文件)。為此Pyodide還引入了用JavaScript編寫的虛擬文件系統。這些虛擬的文件駐留在瀏覽器選項卡性內存中。

通過模擬文件系統和標準科學計算環境的其他功能,將實現通過極少的改動將整個Python科學計算環境挪到Web瀏覽器。整個實現步驟如下:

1、已編譯的Python解釋器為WebAssembly。

2、通過emscripten提供的一系列的JS,用來提供系統仿真。

3、打包文件系統,包含Python解釋器所需的所有文件,包括Python標準庫。

這些文件可能非常大,比如Python本身為21MB,NumPy為7MB,等等依。好在這些軟件包只需要下載一次,然後將它們存儲在瀏覽器的緩存中。

侷限性

通過運行CPython的單元測試對Pyodide做持續測試的,以便了解其通用性。目前還有功能是無法用的,比如Python線程。

由於瀏覽器的安全沙箱限制,一些他功能(比如如低級網絡Socket)也不能正正常工作。

性能問題

在JS虛擬機中運行Python解釋器會有性能損失,但是影響非常小,通過官方基準測試中表明:在Firefox上的比本機慢1x-12x,Chrome上慢1x-16x。還有就是,在Python中運行大量內部循環的代碼往往比依賴於NumPy執行其內部循環的代碼更慢。下面是在Firefox和Chrome對比同硬件本機中運行各種純Python和Numpy基準測試的結果。

Pyodide:將Python科學計算全棧搬到瀏覽器

測試數據和方法鍵github倉庫(iodide-project/)

Python和JS的交互

如果Pyodide只能做到運行Python代碼並寫入標準輸出,雖然不錯,但是沒有實用價值。最重要是實現與瀏覽器API以及其他JS庫的交互。

類型轉換

Pyodide隱式地對Python和JS類型的轉換,下圖展示了兩種語言類型轉換的細節:

Pyodide:將Python科學計算全棧搬到瀏覽器

Python中dicts和對象實例是兩種不同的類型。 dicts只是健值的映射。而對象通常具對象屬性和方法(methond)。在JS中沒有dicts對應的數據結果,通用Object的類型來表示。 Ptdodide在兩者轉化的時候需要用到代理和鴨子類型

代理

代理是另一種語言中變量的包裝器。代理不是簡單地在JS中讀取變量並根據Python結構重寫它,代理保持原始JS變量並"按需"調用其方法。所以任何JS變量,都可以從Python完全訪問。

鴨子類型

鴨子類型是一個原則,而不是實際去問變量"你是鴨子嗎?", "你像鴨子一樣走路嗎?"和"你像鴨子一樣嘎嘎叫嗎?",然後推斷它是否是一隻鴨子。通過鴨子類型Pyodide可以不用馬上考慮推如何轉換JS對象,而將其打包到代理中,讓使用它的代碼決定如何處理它。

當然,這種方法並能一直有效這,鴨子實際上有可能是一隻兔子。Pyodide也提供了明確轉換處理方法。

Pyodide:將Python科學計算全棧搬到瀏覽器

Pyodide使用這種高度交互,深度集成,潛在轉換的方法把Python和JS融為一體。用戶通過Python進行數據處理,然後用JS實現其可視化。

訪問Web API和DOM

代理也是訪問Web API的關鍵。例如,Web API的很大部分位於文檔對象上。你可以通過以下方式從Python中獲得:

from js import document

這會將 JS中的document對象作為代理引入到Python。然後在Python中調用它的方法:

document.getElementById("myElement")

這些都通過代理來查找document對象可以即時執行的操作。 Pyodide不需要包include所有的Web API的完整列表。

多維數組

有一些類型是數據計算必須要的數據類型,比如矩陣計算中用到的多維數組,Pyodide也特別支持這些數據。多維數組是值的集合,這些值都是同一類型,但是數量巨大。Pyodide的多維數組對比Python的列表或JS數組在性能上具有很大優勢。

Python中,NumPy數組是多維數組的最常用的實現。JS中是TypedArrays,它只包含一個數字類型。但是這兩種類型都是一維的,多維數組需要在頂層來構建。

由於現實中這些數組可能會變得非常大,在語言運行時之間複製會非常消耗資源,而且是個非常耗時的操作。

Pyodide中利用多維數組可以無需複製就能在Python和JS之間共享數據。多維數組通常使用少量元數據來實現,這些元數據描述了值的類型,數組的形狀和內存佈局。數據本身通過指向內存中另一個位置的指針在元數據中索引。這個內存地址位於WebAssembly堆區的,可以在JS和Python中訪問。通過在兩種語言中複製元數據,來達到共享其指針指向的WebAssembly堆數據。

Pyodide:將Python科學計算全棧搬到瀏覽器

實時交互式可視化

在客戶端瀏覽器中進行科學數據計算的優點之一是,交互式可視化不必通過網絡進行通信以重新處理和重新顯示其數據。這大大減少了交互的延遲。

完成實時交互可視的工作需要各部分的組件協同工作。下面是一個官方交互式示例動態圖,它展示了使用matplotlib進行對數正態分佈。

Pyodide:將Python科學計算全棧搬到瀏覽器

首先,使用Numpy在Python中生成隨機數據。

其次,在Matplotlib獲取該數據,並使用其內置的軟件渲染器繪製它。

最後通過Pyodide零拷貝數組共享將像素髮送回JS端,在HTML畫布中呈現出來在瀏覽器中將這些像素顯示出來。

用於支持交互性的鼠標和鍵盤事件由從Web瀏覽器調用Python的回調函數處理。

打包

Python科學計算工具棧由很多分散的軟件包構成,這些包協同工作創建一個高效的計算環境。工具棧中最受歡迎的是NumPy(數值數組和基本計算),Scipy(更復雜的通用計算,如線性代數),Matplotlib(可視化)和Pandas(用於表格數據或數據幀處理)。目前Pyodide官方倉庫中包含的包見下圖,更多的包還在不斷完善和添加中:

Pyodide:將Python科學計算全棧搬到瀏覽器

有些包很容易引入進來。通常,使用純Python編寫而沒有其它編譯語言擴展的包都非常容易。像Matplotlib這樣的項目需要使用特殊代碼在HTML畫布中顯示繪圖包為中等難度。而諸如Scipy這樣的包的引入則比較困難。

官方內置的包有限,用戶可以根據需要自引入所需的個性化包,比如:

import numpy as np

上面語法中,Pyodide會引入NumPy庫及其所有依賴項,並將其加載到瀏覽器中。而且包引入瀏覽器後會被緩存起來,下次加載時不需要重複下載。

給Pyodide打包添加新軟件包,目前還不支持動態引入。需要手動添加。據悉未來版本Pyodide將會增加對PyPI的支持。

多語言支持

Pyodide目前已經得到很多語言社區的的關注,目前已經有Julia,R,OCaml,Lua等語言實現良好的瀏覽器運行時。Pyodide還與Iodide等網絡優先工具實現了集成。

Pyodide定義了一個集成級別,用來實現與其他語言及工具的集成:

第一級別:字符串輸出,可以作為基本控制檯REPL(read-eval-print-loop)。

第二級別:支持將基本數據類型(數字,字符串,數組和對象)轉換為JS或從JS轉換。

第三級別:在客戶語言和JS之間共享類實例(帶方法的對象)。這允許Web API訪問。

第四級別:在客戶語言和JS之間共享科學計算相關類型(n維數組和數據框)。

結論

Pydodide提供了我們一種很酷的科學數據編程和用戶體驗。雖然目前還處於實驗性概念驗階段,但是我們已經可以考慮將其做為日常數據科學工作的選擇,並嘗試給它的完善壯大添磚加瓦。


分享到:


相關文章: