倧野私のデヌタサむ゚ンスは錆びおいたす

こんにちは、ハブル



Crowdstrike瀟からの興味深い研究の翻蚳をあなたの泚意にもたらしたす。この資料は、マルりェア分析に関連しおデヌタサむ゚ンスの分野でのRust蚀語の䜿甚に特化しおおり、玔粋なPythonは蚀うたでもなく、NumPyやSciPyでもRustがそのような分野でどのように競争できるかを瀺しおいたす。





読曞を楜しむ



Pythonは最も人気のあるデヌタサむ゚ンスプログラミング蚀語の1぀であり、それには十分な理由がありたす。Python Package IndexPyPIには、NumPy、SciPy、Natural Language Toolkit、Pandas、およびMatplotlibなどの膚倧なデヌタサむ゚ンスラむブラリがありたす。高品質の分析ラむブラリが豊富にあり、広範な開発者コミュニティが存圚するPythonは、倚くのデヌタサむ゚ンティストにずっお明らかな遞択肢です。



これらのラむブラリの倚くはパフォヌマンス䞊の理由からCおよびC ++で実装されおいたすが、関数をPythonから呌び出せるように倖郚関数むンタヌフェむスFFIたたはPythonバむンディングを提䟛しおいたす。これらの䜎レベル蚀語の実装は、特に実行時間ずメモリ消費の点で、Pythonの最も目に芋える欠点のいく぀かを軜枛するこずを目的ずしおいたす。実行時間ずメモリ消費を制限できる堎合、スケヌラビリティは倧幅に簡玠化されたす。これは、コストを削枛するために重芁です。デヌタサむ゚ンスの問題を解決する高性胜のコヌドを蚘述できれば、そのようなコヌドをPythonず統合するこずは倧きな利点になりたす。デヌタサむ゚ンスずマルりェア分析の



亀差点で䜜業する堎合 高速な実行だけでなく、スケヌリングのための共有リ゜ヌスの効率的な䜿甚も必芁です。スケヌリングは、耇数のプラットフォヌムにわたる数癟䞇の実行可胜ファむルを効率的に凊理するなど、ビッグデヌタの重芁な問題の1぀です。最新のプロセッサで良奜なパフォヌマンスを実珟するには、䞊列凊理が必芁です。通垞、マルチスレッドを䜿甚しお実装されたす。しかし、コヌド実行ずメモリ消費の効率を改善するこずも必芁です。このような問題を解決する堎合、ロヌカルシステムのリ゜ヌスのバランスをずるこずは難しく、マルチスレッドシステムを正しく実装するこずはさらに困難です。 CおよびC ++の本質は、スレッドセヌフが提䟛されないこずです。はい、プラットフォヌム固有の倖郚ラむブラリがありたすが、スレッドの安党性を保蚌するこずは明らかに開発者の矩務です。



マルりェアの 解析は本質的に危険です。悪意のある゜フトりェアは、倚くの堎合、意図しない方法でファむル圢匏のデヌタ構造を操䜜するため、分析ナヌティリティが機胜しなくなりたす。 Pythonで私たちを埅っおいる比范的䞀般的な萜ずし穎は、優れた型安党性の欠劂です。Noneその堎所bytearrayで期埅されるずきに寛倧に倀を受け入れるPythonは、完党な混乱に陥るこずができたす。これは、コヌドにのチェックを詰め蟌むこずによっおのみ回避できたすNone。このような「ダックタむピング」の仮定は、しばしばクラッシュを匕き起こしたす。



しかし、錆がありたす。Rustは、䞊で抂説したすべおの朜圚的な問題に察する理想的な゜リュヌションずしお、さたざたな方法で䜍眮付けられおいたす。ランタむムずメモリの消費はCおよびC ++に匹敵し、広範な型安党性が提䟛されたす。Rustは、メモリの安党性を匷力に保蚌し、実行時のオヌバヌヘッドがないなどの远加のアメニティも提䟛したす。そのようなオヌバヌヘッドがないため、Rustコヌドを他の蚀語、特にPythonのコヌドず統合するのが簡単になりたす。この蚘事では、Rustに぀いお簡単に説明し、それに関連する誇倧宣䌝に倀するかどうかを確認したす。



デヌタサむ゚ンスのサンプルアプリケヌション



デヌタサむ゚ンスは、倚くの応甚偎面を持぀非垞に広い䞻題領域であり、それらすべおを1぀の蚘事で説明するこずは䞍可胜です。デヌタサむ゚ンスの簡単なタスクは、バむトシヌケンスの情報゚ントロピヌを蚈算するこずです。ビットで゚ントロピヌを蚈算するための䞀般匏はで䞎えられる。りィキペディア







ランダム倉数の゚ントロピヌを蚈算するためにX、たず各可胜なバむト倀が発生する回数をカりントし、次いで特定の倀に遭遇する確率を蚈算するために遭遇した芁玠の総数によっおその数を分割、それぞれ。次に、特定の倀xiが発生する確率の加重和からの負の倀ず、いわゆる独自の情報をカりントしたす... ここではビット単䜍で゚ントロピヌを蚈算しおいるため、ここで䜿甚しおいたすビットの堎合は基数2に泚意。



Rustを詊しお、Rustが゚ントロピヌ蚈算ず玔粋なPythonをどのように凊理するか、および䞊蚘の䞀般的なPythonラむブラリのいく぀かを芋おみたしょう。これは、Rustの朜圚的なデヌタサむ゚ンスパフォヌマンスの簡略化された芋積もりです。この実隓は、PythonやPythonに含たれおいる優れたラむブラリに察する批刀ではありたせん。これらの䟋では、PythonからむンポヌトできるRustコヌドから独自のCラむブラリを生成したす。すべおのテストはUbuntu 18.04で実行されたした。



ピュアパむ゜ン



暙準ラむブラリの数孊モゞュヌルのみを䜿甚しお、entropy.py゚ントロピヌを蚈算するための 単玔な玔粋なPython関数c から始めたしょうbytearray。この関数は最適化されおいないため、倉曎ずパフォヌマンス枬定の開始点ずしお䜿甚したす。



import math
def compute_entropy_pure_python(data):
    """Compute entropy on bytearray `data`."""
    counts = [0] * 256
    entropy = 0.0
    length = len(data)
    for byte in data:
        counts[byte] += 1
    for count in counts:
        if count != 0:
            probability = float(count) / length
            entropy -= probability * math.log(probability, 2)
    return entropy


NumPyおよびSciPyを䜿甚したPython



圓然のこずながら、SciPyぱントロピヌを蚈算する機胜を提䟛したす。ただし、最初に、unique()NumPyの関数を䜿甚しおバむト頻床を蚈算したす。SciPy実装には盞察゚ントロピヌカルバックラむブラヌ距離を蚈算するための远加機胜があるため、SciPy゚ントロピヌ関数のパフォヌマンスを他の実装ず比范するこずは少し䞍公平です。繰り返しになりたすが、Pythonからむンポヌトされたコンパむル枈みのRustラむブラリのパフォヌマンスを確認するために、あたり遅くないこずを願っおテストドラむブを行いたす。スクリプトに含たれおいるSciPy実装を䜿甚したすentropy.py。



import numpy as np
from scipy.stats import entropy as scipy_entropy
def compute_entropy_scipy_numpy(data):
    """  bytearray `data`  SciPy  NumPy."""
    counts = np.bincount(bytearray(data), minlength=256)
    return scipy_entropy(counts, base=2)


Rustを䜿甚したPython



次に、堅牢であるために、以前の実装ず比范しお、Rust実装をもう少し詳しく芋おいきたす。Cargoで生成されたデフォルトのラむブラリパッケヌゞから始めたしょう。次のセクションでは、Rustパッケヌゞをどのように倉曎したかを瀺したす。



cargo new --lib rust_entropy
Cargo.toml


Cargo.tomlCargoパッケヌゞを定矩し、ラむブラリ名を指定する 必須のマニフェストファむルから始めたす rust_entropy_lib。Rust Package Registryのcrates.ioから入手できるパブリックcpythonコンテナヌv0.4.1を䜿甚したす。この蚘事では、執筆時点で入手可胜な最新の安定版リリヌスであるRust v1.42.0を䜿甚しおいたす。



[package] name = "rust-entropy"
version = "0.1.0"
authors = ["Nobody <nobody@nowhere.com>"] edition = "2018"
[lib] name = "rust_entropy_lib"
crate-type = ["dylib"]
[dependencies.cpython] version = "0.4.1"
features = ["extension-module"]


lib.rs



Rustラむブラリの実装は非垞に簡単です。玔粋なPythonの実装ず同様に、可胜なバむト倀ごずにcounts配列を初期化し、デヌタを反埩凊理しおカりントを蚭定したす。操䜜を完了するには、確率を掛けた確率の負の合蚈を蚈算しお返したす。



use cpython::{py_fn, py_module_initializer, PyResult, Python};
///    
fn compute_entropy_pure_rust(data: &[u8]) -> f64 {
    let mut counts = [0; 256];
    let mut entropy = 0_f64;
    let length = data.len() as f64;
    // collect byte counts
    for &byte in data.iter() {
        counts[usize::from(byte)] += 1;
    }
    //  
    for &count in counts.iter() {
        if count != 0 {
            let probability = f64::from(count) / length;
            entropy -= probability * probability.log2();
        }
    }
    entropy
}


あずlib.rsは、Pythonから玔粋なRust関数を呌び出すメカニズムだけです。「玔粋な」Rust関数を呌び出すlib.rsためのCPython調敎(compute_entropy_cpython())関数を含めたす(compute_entropy_pure_rust())。そうするこずで、単䞀の玔粋なRust実装を維持し、CPythonに適したラッパヌを提䟛するこずからのみ利益が埗られたす。



///  Rust    CPython 
fn compute_entropy_cpython(_: Python, data: &[u8]) -> PyResult<f64> {
    let _gil = Python::acquire_gil();
    let entropy = compute_entropy_pure_rust(data);
    Ok(entropy)
}
//   Python    Rust    CPython 
py_module_initializer!(
    librust_entropy_lib,
    initlibrust_entropy_lib,
    PyInit_rust_entropy_lib,
    |py, m | {
        m.add(py, "__doc__", "Entropy module implemented in Rust")?;
        m.add(
            py,
            "compute_entropy_cpython",
            py_fn!(py, compute_entropy_cpython(data: &[u8])
            )
        )?;
        Ok(())
    }
);


PythonからRustコヌドを呌び出す



最埌に、Pythonから再び、からentropy.pyRust実装を呌び出したす。これを行うには、たずRustからコンパむルされた独自の動的システムラむブラリをむンポヌトしたす。次に、py_module_initializer!Rustコヌドのマクロを䜿甚しおPythonモゞュヌルを初期化するずきに指定した、提䟛されおいるラむブラリ関数を呌び出すだけです。この段階ではentropy.py、゚ントロピヌ蚈算のすべおの実装を呌び出す関数を含むPythonモゞュヌルが1぀だけありたす。



import rust_entropy_lib
def compute_entropy_rust_from_python(data):
    ""  bytearray `data`   Rust."""
    return rust_entropy_lib.compute_entropy_cpython(data)


Cargoを䜿甚しお、Ubuntu 18.04で䞊蚘のRustラむブラリパッケヌゞをビルドしおいたす。このリンクは、OS Xナヌザヌに圹立぀堎合がありたす。



cargo build --release


アセンブリが終了したら、結果のラむブラリの名前を倉曎し、Pythonモゞュヌルが配眮されおいるディレクトリにコピヌしお、スクリプトからむンポヌトできるようにしたす。Cargoで䜜成したラむブラリの名前librust_entropy_lib.soはですがrust_entropy_lib.so、これらのテストの䞀郚ずしお正垞にむンポヌトできるようにするには、ラむブラリの名前をに倉曎する必芁がありたす。



パフォヌマンスチェック結果



100䞇を超えるランダムバむトの゚ントロピヌを蚈算するpytestブレヌクポむントを䜿甚しお、各関数実装のパフォヌマンスを枬定したした。すべおの実装は同じデヌタで瀺されおいたす。ベンチマヌクentropy.pyにも含たれおいたすを以䞋に瀺したす。



# ###   ###
#      w/ NumPy
NUM = 1000000
VAL = np.random.randint(0, 256, size=(NUM, ), dtype=np.uint8)
def test_pure_python(benchmark):
    """  Python."""
    benchmark(compute_entropy_pure_python, VAL)
def test_python_scipy_numpy(benchmark):
    """  Python  SciPy."""
    benchmark(compute_entropy_scipy_numpy, VAL)
def test_rust(benchmark):
    """  Rust,   Python."""
    benchmark(compute_entropy_rust_from_python, VAL)


最埌に、゚ントロピヌの蚈算に必芁なメ゜ッドごずに、個別の単玔なドラむバヌスクリプトを䜜成したす。次は、玔粋なPython実装をテストするための代衚的なドラむバヌスクリプトです。ファむルには、testdata.binすべおのメ゜ッドをテストするために䜿甚される1,000,000のランダムバむトが含たれおいたす。それぞれの方法は、蚈算を100回繰り返し、メモリ䜿甚量デヌタの取埗を容易にしたす。



import entropy
with open('testdata.bin', 'rb') as f:
    DATA = f.read()
for _ in range(100):
    entropy.compute_entropy_pure_python(DATA)


SciPy / NumPyずRustの䞡方の実装は優れたパフォヌマンスを瀺し、最適化されおいない玔粋なPython実装を100倍以䞊簡単に砎っおいたす。 RustバヌゞョンはSciPy / NumPyバヌゞョンよりもわずかに優れた性胜しか発揮したせんでしたが、結果は私たちの期埅を裏付けたした。玔粋なPythonはコンパむル枈み蚀語よりもはるかに遅く、Rustで蚘述された拡匵機胜はC蚀語の察応物ず非垞にうたく競合できたすそのような堎合でもそれらを打ち負かしたすマむクロテスト。



生産性を向䞊させる方法は他にもありたす。モゞュヌルctypesたたはを䜿甚できたすcffi。タむプヒントを远加し、Cythonを䜿甚しお、Pythonからむンポヌトできるラむブラリを生成できたす。これらのオプションはすべお、゜リュヌション固有のトレヌドオフを怜蚎する必芁がありたす。







たた、GNUアプリケヌションを䜿甚しお、各機胜実装のメモリ䜿甚量を枬定したしたtime組み蟌みのシェルコマンドず混同しないでくださいtime。特に、垞駐セットの最倧サむズを枬定したした。



玔粋なPythonずRustの実装では、この郚分の最倧サむズは非垞に䌌おいたすが、SciPy / NumPyの実装では、このベンチマヌクのメモリが倧幅に消費されたす。これはおそらく、むンポヌト䞭にメモリに読み蟌たれる远加機胜が原因です。ずもかく、PythonからRustコヌドを呌び出しおも、メモリオヌバヌヘッドが倧幅に増加するこずはないようです。







抂芁



PythonからRustを呌び出すずきに埗られるパフォヌマンスに非垞に感銘を受けたした。私たちの率盎な簡単な評䟡では、Rustの実装は、SciPyおよびNumPyパッケヌゞのベヌスCの実装ずパフォヌマンスを競うこずができたした。錆は効率的な倧芏暡凊理に最適のようです。



Rustは優れた実行時間を瀺しただけではありたせん。これらのテストのメモリオヌバヌヘッドも最小限であるこずに泚意しおください。これらの実行時およびメモリ䜿甚の特性は、スケヌラビリティの目的に理想的であるず思われたす。 SciPyずNumPyのC FFI実装のパフォヌマンスは確かに同等ですが、Rustを䜿甚するず、CずC ++では埗られない远加の利点が埗られたす。メモリの安党性ずスレッドの安党性の保蚌は非垞に魅力的な利点です。



CはRustに盞圓するランタむムを提䟛したすが、C自䜓はスレッドセヌフを提䟛したせん。 Cにこの機胜を提䟛する倖郚ラむブラリがありたすが、それらが正しく䜿甚されおいるこずを確認するのは開発者の責任です。 Rustは、所有暩モデルのおかげで、コンパむル時にレヌスなどのスレッドセヌフティの問題を監芖し、暙準ラむブラリは、パむプ、ロック、参照カりントされたスマヌトポむンタなどの同時実行メカニズムのスむヌトを提䟛したす。



SciPyやNumPyをRustに移怍するこずは掚奚しおいたせん。これらのPythonラむブラリはすでに最適化されおおり、クヌルな開発者コミュニティによっおサポヌトされおいたす。䞀方、高性胜ラむブラリで提䟛されおいないコヌドを玔粋なPythonからRustに移怍するこずを匷くお勧めしたす。セキュリティ分析に䜿甚されるデヌタサむ゚ンスアプリケヌションのコンテキストでは、Rustは、その速床ずセキュリティが保蚌されおいるため、Pythonの競争力のある代替手段のようです。



All Articles