国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

WebブラウザのLAPACK

Dec 30, 2024 am 10:05 AM

この投稿はもともと Quansight Labs ブログで公開されたもので、Quansight の許可を得て変更され、ここに再公開されています。

Web アプリケーションは、高性能科學(xué)計(jì)算と AI 対応のエンドユーザー エクスペリエンスの新たなフロンティアとして急速に臺(tái)頭しています。 ML/AI 革命を支えているのは、線形方程式とそのベクトル空間および行列による表現(xiàn)に関する數(shù)學(xué)の一分野である線形代數(shù)です。 LAPACK ("Linear Algebra Package") は數(shù)値線形代數(shù)の基本的なソフトウェア ライブラリであり、一般的な行列演算の堅(jiān)牢で実戦テストされた実裝を提供します。 。 LAPACK はほとんどの數(shù)値計(jì)算プログラミング言語およびライブラリの基本コンポーネントであるにもかかわらず、Web 固有の制約に合わせた包括的で高品質(zhì)な LAPACK 実裝はまだ実現(xiàn)していません。それは…今までです。

今年の初め、私は幸運(yùn)なことに、Quansight の公益部門であり、科學(xué)的な Python エコシステムのリーダーである Quansight Labs で夏季インターンとして働くことができました。インターンシップ中に、私は stdlib に初期 LAPACK サポートを追加することに取り組みました。stdlib は、C と JavaScript で記述され、Web ブラウザーや他の Web ネイティブ環(huán)境 (Node.js や Deno など) で使用するために最適化された科學(xué)計(jì)算用の基本ライブラリです。このブログ投稿では、私のこれまでの道のり、予期されたおよび予期せぬ (!) 課題、そして今後の道のりについて説明します。私の希望は、少しの幸運(yùn)があれば、この成果が Web ブラウザーを數(shù)値計(jì)算と機(jī)械學(xué)習(xí)のための一流の環(huán)境にするための重要な構(gòu)成要素を提供し、より強(qiáng)力な AI 対応 Web アプリケーションの未來の前兆となることです。

面白いと思いませんか?行きましょう!

標(biāo)準(zhǔn)ライブラリとは何ですか?

LAPACK に詳しいこのブログの読者は、Web テクノロジーの荒々しい世界には詳しくない可能性があります。數(shù)値計(jì)算および科學(xué)計(jì)算の世界に屬し、科學(xué) Python エコシステムに精通している人にとって、stdlib を NumPy と SciPy の型をとったオープンソースの科學(xué)計(jì)算ライブラリとして考えるのが最も簡(jiǎn)単です。多次元配列データ構(gòu)造と、數(shù)學(xué)、統(tǒng)計(jì)、線形代數(shù)に関連するルーチンを提供しますが、主なスクリプト言語として Python ではなく JavaScript を使用します。そのため、stdlib は Web エコシステムとそのアプリケーション開発パラダイムに重點(diǎn)を置いています。この焦點(diǎn)には、いくつかの興味深い設(shè)計(jì)とプロジェクト アーキテクチャの決定が必要であり、數(shù)値計(jì)算用に設(shè)計(jì)された従來のライブラリと比較した場(chǎng)合、stdlib がかなりユニークになります。

NumPy を例に挙げると、NumPy は単一のモノリシック ライブラリであり、OpenBLAS などのオプションのサードパーティ依存関係を除くすべてのコンポーネントが単一の分割不可能なユニットを形成します。すべての NumPy をインストールせずに、単純に配列操作用の NumPy ルーチンをインストールすることはできません。 NumPy の ndarray オブジェクトとその操作ルーチンのいくつかだけを必要とするアプリケーションをデプロイする場(chǎng)合、NumPy をすべてインストールしてバンドルすることは、かなりの量の「デッド コード」を含めることを意味します。 Web 開発用語で言うと、NumPy は「ツリーシェイク可能」ではないと言えます。通常の NumPy インストールの場(chǎng)合、これは少なくとも 30 MB のディスク容量を意味し、すべてのデバッグ ステートメントを除外するカスタマイズされたビルドの場(chǎng)合は少なくとも 15 MB のディスク容量を意味します。 SciPy の場(chǎng)合、これらの數(shù)値はそれぞれ 130MB と 50MB にまで膨れ上がる可能性があります。言うまでもなく、Web アプリケーションにほんの少數(shù)の機(jī)能を提供するために 15MB のライブラリを出荷することは、特にネットワーク接続が不十分なデバイスやメモリの制約があるデバイスに Web アプリケーションを展開する必要がある開発者にとっては、初心者には適していません。

Web アプリケーション開発特有の制約を考慮して、stdlib は設(shè)計(jì)にボトムアップ アプローチを採用しており、コードベースの無関係な未使用部分とは獨(dú)立して機(jī)能のすべてのユニットをインストールして使用できます。 stdlib は、分解可能なソフトウェア アーキテクチャと徹底的なモジュール性を採用することで、必要な API セットとその明示的な依存関係を超える余分なコードをほとんどまたはまったく使用せずに、必要なものを正確にインストールして使用できる機(jī)能をユーザーに提供します。これにより、メモリ フットプリント、バンドルの削減が保証されます。サイズを拡大し、展開を高速化します。

例として、2 つの行列スタック (つまり、3 次元立方體の 2 次元スライス) を操作していて、1 つおきのスライスを選択して、一般的な BLAS 演算 y = a * x を?qū)g行するとします。ここで、x と y は ndarray で、a はスカラー定數(shù)です。 NumPy を使用してこれを行うには、まず NumPy
をすべてインストールします。

pip install numpy

その後、さまざまな操作を?qū)g行します

# Import all of NumPy:
import numpy as np

# Define arrays:
x = np.asarray(...)
y = np.asarray(...)

# Perform operation:
y[::2,:,:] += 5.0 * x[::2,:,:]

stdlib を使用すると、プロジェクトをモノリシック ライブラリとしてインストールできることに加えて、さまざまな機(jī)能ユニットを個(gè)別のパッケージとしてインストールできます

npm install @stdlib/ndarray-fancy @stdlib/blas-daxpy

その後、さまざまな操作を?qū)g行します

// Individually import desired functionality:
import FancyArray from '@stdlib/ndarray-fancy';
import daxpy from '@stdlib/blas-daxpy';

// Define ndarray meta data:
const shape = [4, 4, 4];
const strides = [...];
const offset = 0;

// Define arrays using a "lower-level" fancy array constructor:
const x = new FancyArray('float64', [...], shape, strides, offset, 'row-major');
const y = new FancyArray('float64', [...], shape, strides, offset, 'row-major');

// Perform operation:
daxpy(5.0, x['::2,:,:'], y['::2,:,:']);

重要なのは、stdlib の 4,000 を超えるパッケージのいずれかを個(gè)別にインストールできるだけでなく、関連する GitHub リポジトリをフォークすることで、それらのパッケージのいずれかを修正、改善、リミックスすることもできることです (例: @stdlib/ndarray-fancy を參照) )。 stdlib では、抽象化層と依存関係ツリーを明示的に定義することにより、アプリケーションに適切な抽象化層を自由に選択できます。ある意味、これは単純なアイデアであり、従來の科學(xué)ソフトウェア ライブラリの設(shè)計(jì)に慣れている人にとっては、おそらく型破りなアイデアですが、Web プラットフォームと緊密に統(tǒng)合されると、強(qiáng)力な結(jié)果をもたらし、刺激的な新しい可能性が生まれます。

WebAssembly についてはどうでしょうか?

なるほど、興味が湧いたのかもしれません。 stdlib は興味深いようです。しかし、これは Web ブラウザの LAPACK とどのような関係があるのでしょうか?さて、この夏の私たちの目標(biāo)の 1 つは、LAPACK を Web に導(dǎo)入する際に、stdlib の精神 (1 つのことを?qū)g行し、1 つのことをうまく実行する、小規(guī)模で範(fàn)囲が狹いパッケージ) を適用することでした。

でも、ちょっと待ってください!それは極端な取り組みです。 LAPACK には約 1,700 のルーチンがあり、その 10% を妥當(dāng)な期間內(nèi)に実裝することは大きな課題です。 LAPACK を、C、Go、Rust などのプログラミング言語用の移植可能なコンパイル ターゲットである WebAssembly にコンパイルするだけで、Web 上でのデプロイメントが可能になり、それで終わりにする方がよいのではないでしょうか?

殘念ながら、このアプローチにはいくつかの問題があります。

  1. Fortran を WebAssembly にコンパイルすることは、現(xiàn)在も開発が活発に行われている領(lǐng)域です (1、2、3、4、および 5 を參照)。この投稿の時(shí)點(diǎn)では、一般的なアプローチは、f2c を使用して Fortran を C にコンパイルし、次に別のコンパイル手順を?qū)g行して C を WebAssembly に変換することです。ただし、f2c は Fortran 77 のみを完全にサポートしており、生成されたコードには広範(fàn)なパッチが必要であるため、このアプローチには問題があります。 LLVM ベースの Fortran コンパイラーの開発作業(yè)が進(jìn)行中ですが、ギャップと複雑なツールチェーンが殘っています。
  2. Web アプリケーションのモノリシック ライブラリに関する上記の議論でほのめかしたように、LAPACK の膨大さが問題の一部です。コンパイルの問題が解決したとしても、1 つまたは 2 つの LAPACK ルーチンのみを使用する必要がある Web アプリケーションに、すべての LAPACK を含む単一の WebAssembly バイナリを含めることは、かなりのデッド コードを意味し、その結(jié)果、読み込み時(shí)間が遅くなり、メモリ消費(fèi)量が増加します。
  3. 個(gè)々の LAPACK ルーチンをスタンドアロン WebAssembly バイナリにコンパイルすることもできますが、複數(shù)のスタンドアロン バイナリに共通の依存関係からの重複したコードが含まれる可能性があるため、バイナリが肥大化する可能性があります。バイナリの肥大化を軽減するには、モジュール分割の実行を試みることができます。このシナリオでは、まず共通の依存関係を共有コードを含むスタンドアロン バイナリに抽出し、次に個(gè)々の API に対して個(gè)別のバイナリを生成します。場(chǎng)合によっては適していますが、このアプローチでは、1 つまたは複數(shù)のモジュールのエクスポートと 1 つまたは複數(shù)の他のモジュールのインポートをつなぎ合わせることによって、ロード時(shí)に個(gè)々の WebAssembly モジュールをリンクする必要があるため、すぐに扱いにくくなる可能性があります。これは面倒なだけでなく、WebAssembly ルーチンがインポートされたエクスポートを呼び出すときに、WebAssembly 內(nèi)に留まるのではなく JavaScript にクロスオーバーする必要があるため、この方法ではパフォーマンスの低下も伴います。複雑に聞こえますか?そうです!
  4. スカラー入力引數(shù)のみで動(dòng)作する WebAssembly モジュール (単一の數(shù)値の正弦を計(jì)算するなど) とは別に、すべての WebAssembly モジュール インスタンスは、64KiB の固定増分で割り當(dāng)てられる WebAssembly メモリ (つまり、「ページ」) に関連付けられている必要があります。 ")。そして重要なのは、このブログ投稿の時(shí)點(diǎn)では、WebAssembly メモリは増加することだけがあり、減少することはありません?,F(xiàn)在、メモリをホストに解放するメカニズムがないため、WebAssembly アプリケーションのメモリ フットプリントは増加するだけです。これら 2 つの側(cè)面が組み合わされると、使用されないメモリが割り當(dāng)てられる可能性が高まり、メモリ リークが蔓延します。
  5. 最後に、WebAssembly は強(qiáng)力ではありますが、より急峻な學(xué)習(xí)曲線と、急速に進(jìn)化することが多いツールチェーンのより複雑なセットを必要とします。エンドユーザー アプリケーションでは、Web ネイティブで動(dòng)的にコンパイルされるプログラミング言語である JavaScript と WebAssembly の間のインターフェイスは、特に手動(dòng)でメモリ管理を?qū)g行する必要がある場(chǎng)合に、さらに複雑さをもたらします。

最後の點(diǎn)を説明するために、BLAS ルーチン daxpy に戻りましょう。このルーチンは、演算 y = a*x y を?qū)g行します。ここで、x と y はストライドされたベクトル、a はスカラー定數(shù)です。 C で実裝された場(chǎng)合、基本的な実裝は次のコード スニペットのようになります。

pip install numpy

WebAssembly にコンパイルし、WebAssembly バイナリを Web アプリケーションにロードした後、JavaScript から c_daxpy ルーチンを呼び出す前に、一連の手順を?qū)g行する必要があります。まず、新しい WebAssembly モジュールをインスタンス化する必要があります。

# Import all of NumPy:
import numpy as np

# Define arrays:
x = np.asarray(...)
y = np.asarray(...)

# Perform operation:
y[::2,:,:] += 5.0 * x[::2,:,:]

次に、モジュール メモリを定義し、新しい WebAssembly モジュール インスタンスを作成する必要があります。

npm install @stdlib/ndarray-fancy @stdlib/blas-daxpy

モジュール インスタンスを作成した後、エクスポートされた BLAS ルーチンを呼び出すことができます。ただし、データがモジュール メモリの外部で定義されている場(chǎng)合は、まずそのデータをメモリ インスタンスにコピーする必要があり、常にリトル エンディアンのバイト オーダーでコピーする必要があります。

// Individually import desired functionality:
import FancyArray from '@stdlib/ndarray-fancy';
import daxpy from '@stdlib/blas-daxpy';

// Define ndarray meta data:
const shape = [4, 4, 4];
const strides = [...];
const offset = 0;

// Define arrays using a "lower-level" fancy array constructor:
const x = new FancyArray('float64', [...], shape, strides, offset, 'row-major');
const y = new FancyArray('float64', [...], shape, strides, offset, 'row-major');

// Perform operation:
daxpy(5.0, x['::2,:,:'], y['::2,:,:']);

データがモジュール メモリに書き込まれたので、c_daxpy ルーチンを呼び出すことができます。

void c_daxpy(const int N, const double alpha, const double *X, const int strideX, double *Y, const int strideY) {
    int ix;
    int iy;
    int i;
    if (N <= 0) {
        return;
    }
    if (alpha == 0.0) {
        return;
    }
    if (strideX < 0) {
        ix = (1-N) * strideX;
    } else {
        ix = 0;
    }
    if (strideY < 0) {
        iy = (1-N) * strideY;
    } else {
        iy = 0;
    }
    for (i = 0; i < N; i++) {
        Y[iy] += alpha * X[ix];
        ix += strideX;
        iy += strideY;
    }
    return;
}

そして最後に、視覚化やさらなる分析のために、WebAssembly メモリ「ポインター」(つまり、バイト オフセット) をサポートしていないダウンストリーム ライブラリ (D3 など) に結(jié)果を渡す必要がある場(chǎng)合は、モジュールからデータをコピーする必要があります。メモリを元の出力配列に戻します。

const binary = new UintArray([...]);

const mod = new WebAssembly.Module(binary);

y = a*x y を計(jì)算するだけでも大変な作業(yè)です。対照的に、次のコード スニペットのように見える?yún)g純な JavaScript 実裝と比較してください。

// Initialize 10 pages of memory and allow growth to 100 pages:
const mem = new WebAssembly.Memory({
    'initial': 10,  // 640KiB, where each page is 64KiB
    'maximum': 100  // 6.4MiB
});

// Create a new module instance:
const instance = new WebAssembly.Instance(mod, {
    'env': {
        'memory': mem
    }
});

JavaScript 実裝を使用すると、上記の WebAssembly の例で必要なデータ移動(dòng)を行わずに、外部定義されたデータを使用して daxpy を直接呼び出すことができます。

// External data:
const xdata = new Float64Array([...]);
const ydata = new Float64Array([...]);

// Specify a vector length:
const N = 5;

// Specify vector strides (in units of elements):
const strideX = 2;
const strideY = 4;

// Define pointers (i.e., byte offsets) for storing two vectors:
const xptr = 0;
const yptr = N * 8; // 8 bytes per double

// Create a DataView over module memory:
const view = new DataView(mem.buffer);

// Resolve the first indexed elements in both `xdata` and `ydata`:
let offsetX = 0;
if (strideX < 0) {
    offsetX = (1-N) * strideX;
}
let offsetY = 0;
if (strideY < 0) {
    offsetY = (1-N) * strideY;
}

// Write data to the memory instance:
for (let i = 0; i < N; i++) {
    view.setFloat64(xptr+(i*8), xdata[offsetX+(i*strideX)], true);
    view.setFloat64(yptr+(i*8), ydata[offsetY+(i*strideY)], true);
}

少なくともこの場(chǎng)合、WebAssembly のアプローチは人間工學(xué)的ではないだけでなく、必要なデータ移動(dòng)を考えると予想されるとおり、次の図に示すようにパフォーマンスにも悪影響があります。

LAPACK in your web browser

図 1: 配列の長(zhǎng)さ (x 軸) を増加させた場(chǎng)合の、BLAS ルーチン daxpy の stdlib の C、JavaScript、および WebAssembly (Wasm) 実裝のパフォーマンスの比較。 Wasm (コピー) ベンチマークでは、入出力データが Wasm メモリとの間でコピーされるため、パフォーマンスが低下します。

上の図では、配列の長(zhǎng)さを増加させた場(chǎng)合の BLAS ルーチン daxpy の stdlib の C、JavaScript、および WebAssembly (Wasm) 実裝のパフォーマンス比較を x 軸に沿って列挙して表示しています。 y 軸は、ベースライン C 実裝に対する正規(guī)化されたレートを示します。 Wasm ベンチマークでは、入出力データは WebAssembly モジュール メモリ內(nèi)で直接割り當(dāng)ておよび操作されます。Wasm (コピー) ベンチマークでは、上で説明したように、入出力データは WebAssembly モジュール メモリとの間でコピーされます。チャートから次のことがわかります:

  1. 一般に、高度に最適化されたジャストインタイム (JIT) コンパイラーのおかげで、JavaScript コードは、慎重に作成しても、ネイティブ コードよりも 2 ~ 3 倍遅くしか実行できません。この結(jié)果は、緩やかに型指定され、動(dòng)的にコンパイルされるプログラミング言語としては印象的であり、少なくとも daxpy では、配列の長(zhǎng)さが変化しても一貫性が保たれます。
  2. データ サイズが増加し、WebAssembly モジュールで費(fèi)やされる時(shí)間が増加すると、WebAssembly の速度はネイティブ (約 1.5 倍) に近づく可能性があります。この結(jié)果は、予想される WebAssembly のパフォーマンスとより一般的に一致します。
  3. WebAssembly はネイティブに近い速度を達(dá)成できますが、daxpy で観察されたように、データ移動(dòng)要件がパフォーマンスに悪影響を與える可能性があります。このような場(chǎng)合、そのような要件を回避する適切に作成された JavaScript 実裝により、daxpy の場(chǎng)合と同様に、それ以上ではないにしても同等のパフォーマンスを達(dá)成できます。

全體的に、WebAssembly はパフォーマンスを向上させることができます。ただし、このテクノロジーは特効薬ではないため、望ましい利益を?qū)g現(xiàn)するには慎重に使用する必要があります。また、優(yōu)れたパフォーマンスを提供する場(chǎng)合でも、そのような利益は、複雑さの増加、潛在的にバンドル サイズが大きくなる可能性、およびより複雑なツールチェーンによるコストとバランスを取る必要があります。多くのアプリケーションでは、プレーンな JavaScript 実裝で問題なく機(jī)能します。

根本的なモジュール化

LAPACK 全體を WebAssembly にコンパイルするだけで訴訟を起こして終わりにしたので、あとはどうなるでしょうか?そうですね、stdlib の理念を受け入れるつもりなら、抜本的なモジュール化が必要になります。

ラディカルなモジュール性を採用するということは、何が最善であるかは高度にコンテキストに依存しており、ユーザー アプリケーションのニーズと制約に応じて、開発者は適切な抽象化を選択する柔軟性が必要であることを認(rèn)識(shí)することです。開発者が Node.js アプリケーションを作成している場(chǎng)合、優(yōu)れたパフォーマンスを?qū)g現(xiàn)するために、OpenBLAS、Intel MKL、Apple Accelerate などのハードウェアに最適化されたライブラリにバインドする必要がある場(chǎng)合があります。開発者が少數(shù)の數(shù)値ルーチンを必要とする Web アプリケーションをデプロイしている場(chǎng)合、JavaScript がその作業(yè)に適したツールである可能性があります。また、開発者が大規(guī)模でリソースを大量に消費(fèi)する WebAssembly アプリケーション (畫像編集やゲーム エンジンなど) に取り組んでいる場(chǎng)合、より大きなアプリケーションの一部として個(gè)々のルーチンを簡(jiǎn)単にコンパイルできることが最も重要になります。つまり、根本的にモジュール化された LAPACK が必要なのです。

私の使命は、そのような取り組みの基礎(chǔ)を築き、問題點(diǎn)を解決してギャップを見つけ、できればウェブ上の高性能線形代數(shù)に數(shù)歩近づけることでした。しかし、根本的なモジュール化とはどのようなものでしょうか?すべては、機(jī)能の基本単位である パッケージ から始まります。

stdlib 內(nèi)のすべてのパッケージは、獨(dú)自のスタンドアロンのものであり、同時(shí)ローカライズされたテスト、ベンチマーク、サンプル、ドキュメント、ビルド ファイル、および関連するメタデータ (依存関係の列挙を含む) を含み、外部との明確な API サーフェスを定義します。 。 LAPACK サポートを stdlib に追加するには、次の構(gòu)造を持つ LAPACK ルーチンごとに個(gè)別のスタンドアロン パッケージを作成することを意味します。

pip install numpy

簡(jiǎn)単に言うと、

  • benchmark: リファレンス実裝 (つまり、リファレンス LAPACK) と比較してパフォーマンスを評(píng)価するためのマイクロベンチマークを含むフォルダー。
  • docs: REPL ヘルプ テキストや型指定された API 署名を定義する TypeScript 宣言などの補(bǔ)助ドキュメントが含まれるフォルダーです。
  • : 実行可能なデモンストレーション コードを含むフォルダー。ドキュメントとして機(jī)能するだけでなく、開発者が実裝動(dòng)作の健全性をチェックするのにも役立ちます。
  • include: C ヘッダー ファイルを含むフォルダー。
  • lib: JavaScript ソース実裝を含むフォルダー。index.js はパッケージのエントリ ポイントとして機(jī)能し、他の *.js ファイルは內(nèi)部実裝モジュールを定義します。
  • src: C および Fortran ソース実裝を含むフォルダー。各モジュール式 LAPACK パッケージには、わずかに変更された Fortran リファレンス実裝 (F77 から自由形式 Fortran) が含まれている必要があります。 C ファイルには、Fortran 參照実裝に従うプレーン C 実裝、Fortran 參照実裝を呼び出すためのラッパー、サーバー側(cè)アプリケーションでハードウェア最適化ライブラリ (OpenBLAS など) を呼び出すためのラッパー、およびコンパイル済みライブラリを呼び出すためのネイティブ バインディングが含まれます。 Node.js の JavaScript または互換性のあるサーバーサイド JavaScript ランタイムからの C。
  • test: JavaScript とネイティブ実裝の両方で予期される動(dòng)作をテストするための単體テストが含まれるフォルダー。ネイティブ実裝のテストは JavaScript で記述され、JavaScript と C/Fortran 間の相互運(yùn)用にネイティブ バインディングを活用します。
  • binding.gyp/include.gypi: Node.js ネイティブ アドオンをコンパイルするためのファイルをビルドします。これは、JavaScript とネイティブ コード間のブリッジを提供します。
  • manifest.json: stdlib の內(nèi)部 C および Fortran でコンパイルされたソース ファイル パッケージ管理のための構(gòu)成ファイル。
  • package.json: 外部パッケージの依存関係の列挙と、ブラウザーベースの Web アプリケーションで使用するプレーンな JavaScript 実裝へのパスを含む、パッケージ メタ データを含むファイル。
  • README.md: パッケージの主要なドキュメントが含まれるファイル。これには、JavaScript と C インターフェイスの両方の API シグネチャと例が含まれます。

stdlib の厳しいドキュメントとテスト要件を考慮すると、各ルーチンのサポートを追加するのはかなりの作業(yè)量ですが、最終結(jié)果は堅(jiān)牢で高品質(zhì)で、最も重要なことに、科學(xué)計(jì)算の基盤として機(jī)能するのに適したモジュール式コードになります?,F(xiàn)代のウェブ上で。しかし、前置きはこれで十分です!本題に入りましょう!

多段階のアプローチ

BLAS サポートを stdlib に追加した以前の取り組みに基づいて、LAPACK サポートを追加するときにも同様の多段階アプローチに従うことにしました。このアプローチでは、最初に JavaScript 実裝とそれに関連するテストとドキュメントを優(yōu)先し、次にテストとドキュメントが存在するようになります。 、C および Fortran 実裝、およびハードウェア最適化ライブラリへの関連するネイティブ バインディングをバックフィルします。このアプローチにより、構(gòu)築ツールチェーンとパフォーマンスの雑草に飛び込む前に、いわばボード上でいくつかの初期のポイントを設(shè)定し、ユーザーの前に API を迅速に提供し、堅(jiān)牢なテスト手順とベンチマークを確立し、ツールと自動(dòng)化の潛在的な手段を調(diào)査することができます。最適化。しかし、どこから始めればよいのでしょうか?

どの LAPACK ルーチンを最初にターゲットにするかを決定するために、LAPACK の Fortran ソース コードを解析してコール グラフを生成しました。これにより、各 LAPACK ルーチンの依存関係ツリーを推測(cè)できるようになりました。次に、グラフを手にしてトポロジカル ソートを?qū)g行しました。これにより、依存関係のないルーチン、および必然的に他のルーチンの構(gòu)成要素となるルーチンを特定するのに役立ちました。特定の高レベルのルーチンを選択して逆方向に作業(yè)する深さ優(yōu)先のアプローチでは、特定の機(jī)能を?qū)g現(xiàn)できますが、そのようなアプローチでは、ますます複雑になるルーチンを?qū)g裝しようとして行き詰まってしまう可能性があります。グラフの「葉」に焦點(diǎn)を當(dāng)てることで、一般的に使用されるルーチン (つまり、度數(shù) が高いルーチン) に優(yōu)先順位を付けることができ、後から複數(shù)の高レベルのルーチンを提供できるようにすることで効果を最大化できます。私の取り組み、または他の貢獻(xiàn)者による取り組み。

計(jì)畫を立てたので、仕事に取り掛かることに興奮していました。最初のルーチンとして、dalaswp を選択しました。これは、提供されたピボット インデックスのリストに従って一般的な長(zhǎng)方形行列上で一連の行交換を?qū)g行し、LAPACK の LU 分解ルーチンの重要な構(gòu)成要素です。そしてそこから私の挑戦が始まりました...

課題

レガシー Fortran

Quansight Labs でのインターンシップに參加する前、私は、LLVM 上に構(gòu)築された最新の対話型 Fortran コンパイラーである LFortran に定期的に貢獻(xiàn)しており (そして今でも!)、自分の Fortran スキルにはかなり自信を持っていました。しかし、私の最初の課題の 1 つは、現(xiàn)在「レガシー」と考えられている Fortran コードを単純に理解することでした。以下に最初の 3 つのハードルを取り上げます。

書式設(shè)定

LAPACK はもともと FORTRAN 77 (F77) で書かれました。ライブラリはバージョン 3.2 (2008) で Fortran 90 に移行されましたが、従來の規(guī)約がリファレンス実裝にまだ殘っています。これらの規(guī)則の中で最も顕著なものの 1 つは書式設(shè)定です。

F77 プログラムを作成する開発者は、パンチカードから継承した固定形式のレイアウトを使用してプログラムを作成しました。このレイアウトには、文字列の使用に関する?yún)棨筏ひⅳ辘蓼筏?

  • 行全體を占めるコメントは、最初の列の特殊文字 (*、!、または C など) で始まる必要があります。
  • コメント以外の行の場(chǎng)合、1) 最初の 5 列は空白であるか、數(shù)値ラベルが含まれている必要があり、2) 6 列目は継続文字用に予約されており、3) 実行可能ステートメントは 7 列目から開始する必要があり、4) それ以降のコードは次のとおりです。列 72 は無視されました。

Fortran 90 は、列と行の長(zhǎng)さの制限を取り除き、 ! に落ち著いたフリーフォームレイアウトを?qū)毪筏蓼筏?。コメント文字として。次のコード スニペットは、LAPACK ルーチン dlacpy のリファレンス実裝を示しています。

pip install numpy

次のコード スニペットは同じルーチンを示していますが、Fortran 90 で導(dǎo)入されたフリー フォーム レイアウトを使用して実裝されています。

# Import all of NumPy:
import numpy as np

# Define arrays:
x = np.asarray(...)
y = np.asarray(...)

# Perform operation:
y[::2,:,:] += 5.0 * x[::2,:,:]

ご覧のとおり、列制限を削除し、指定子をすべて大文字で記述するという F77 規(guī)則から離れることにより、最新の Fortran コードはより一貫性が高く、読みやすくなりました。

ラベル付き制御構(gòu)造

LAPACK ルーチンのもう 1 つの一般的な方法は、ラベル付きの制御構(gòu)造の使用です。たとえば、ラベル 10 が対応する CONTINUE.
と一致する必要がある次のコード スニペットを考えてみましょう。

npm install @stdlib/ndarray-fancy @stdlib/blas-daxpy

Fortran 90 では、do ループを終了するために end do を使用できるようにすることで、この慣行の必要性がなくなり、コードの可読性が向上しました。この変更は、上記で提供されている dlacpy のフリーフォーム バージョンに示されています。

想定されるサイズの配列

さまざまなサイズの配列を柔軟に処理できるようにするために、LAPACK ルーチンは通常、想定されたサイズの配列を操作します。上記の dlacpy ルーチンでは、入力行列 A は、式 A(LDA, *) に従って仮定されたサイズを持つ 2 次元配列として宣言されます。この式は、A に LDA 行數(shù)があることを宣言し、* をプレースホルダーとして使用して、2 番目の次元のサイズが呼び出し側(cè)プログラムによって決定されることを示します。

サイズ引継ぎ配列を使用すると、コンパイラが未指定の次元で境界チェックを?qū)g行できなくなることが起こります。したがって、現(xiàn)在のベスト プラクティスは、境界外のメモリ アクセスを防ぐために、明示的なインターフェイスと想定形狀配列 (例: A(LDA,:)) を使用することです。これは、部分行列を他の関數(shù)に渡す必要がある場(chǎng)合、形狀を仮定した配列の使用は問題となる可能性があることを示しています。そうするためにはスライスが必要となり、コンパイラーが配列データの內(nèi)部コピーを作成することがよくあります。

Fortran 95 への移行

言うまでもなく、LAPACK の慣例に適応し、LAPACK の考え方を取り入れるまでには時(shí)間がかかりました。しかし、純粋主義者の私としては、どうせルーチンを移植するのであれば、コードの可読性と將來のメンテナンスを改善するために、移植に成功したルーチンを少なくともより現(xiàn)代的な時(shí)代に導(dǎo)入したいと考えていました。そこで、stdlib のメンテナと話し合った後、ルーチンを Fortran 95 に移行することにしました。これは、最新かつ最高の Fortran バージョンではありませんが、元の実裝のルック アンド フィールの維持と、(これで十分) 下位互換性があり、新しい構(gòu)文機(jī)能を活用できます。

テスト範(fàn)囲

LAPACK サポートを追加するボトムアップ アプローチを追求する場(chǎng)合の問題の 1 つは、下位レベルのユーティリティ ルーチンの明示的な単體テストが LAPACK に存在しないことが多いことです。 LAPACK のテスト スイートは主に階層的なテスト哲學(xué)を採用しており、上位レベルのルーチンをテストすることで、依存する下位レベルのルーチンがワークフロー全體の一部として正しく機(jī)能していることを確認(rèn)します。すべてのルーチンにテストを追加すると、保守の負(fù)擔(dān)と LAPACK のテスト フレームワークの複雑さが増大する可能性があるため、下位レベルのルーチンの単體テストよりも統(tǒng)合テストに重點(diǎn)を置くのが合理的であると主張する人もいます。これは、以前のテストに簡(jiǎn)単に依存できないことを意味します。単體テストには技術(shù)が必要であり、下位レベルのルーチンごとに包括的なスタンドアロン単體テストを自分たちで考え出す必要があります。

ドキュメント

テスト カバレッジと同様に、LAPACK 自體の外で、下位レベルのルーチンの使用を示す実際の文書化された例を見つけることは困難でした。 LAPACK ルーチンの前には常に、入力引數(shù)と考えられる戻り値の説明を提供するドキュメント コメントが付いていますが、コード例がなければ、特に特殊な行列を扱う場(chǎng)合、予想される入力値と出力値を視覚化して把握するのは困難な場(chǎng)合があります。単體テストや文書化されたサンプルが存在しないからといって世界が終わるわけではありませんが、stdlib に LAPACK サポートを追加するのは予想以上に大変な作業(yè)になることを意味します。ベンチマーク、テスト、サンプル、ドキュメントを作成するには、より多くの時(shí)間と労力が必要となり、インターンシップ中に実裝できるルーチンの數(shù)が制限される可能性がありました。

メモリレイアウト

行列要素を線形メモリに格納する場(chǎng)合、列を連続して格納するか、行を連続して格納するかの 2 つの選択肢があります (図 2 を參照)。前者のメモリ レイアウトは 列優(yōu)先 順序と呼ばれ、後者は 行優(yōu)先 順序と呼ばれます。

LAPACK in your web browser

図 2: (a) 列優(yōu)先 (Fortran スタイル) または (b) 行優(yōu)先 (C スタイル) のいずれかの順序で行列要素を線形メモリに保存する様子を示す回路図。どのレイアウトを使用するかの選択は、主に慣例によるものです。

どのレイアウトを使用するかの選択は、主に慣例の問題です。たとえば、Fortran は要素を列優(yōu)先の順序で格納し、C は要素を行優(yōu)先の順序で格納します。 NumPy や stdlib などの高レベルのライブラリは、列優(yōu)先と行優(yōu)先の両方をサポートしているため、配列の作成中に多次元配列のレイアウトを構(gòu)成できます。

pip install numpy

どちらのメモリ レイアウトも本質(zhì)的に他方より優(yōu)れているわけではありませんが、最適なパフォーマンスを確保するには、基礎(chǔ)となるストレージ モデルの規(guī)則に従ってシーケンシャル アクセスを保証するようにデータを配置することが重要です。最新の CPU は、シーケンシャル データを非シーケンシャル データよりも効率的に処理できます。これは主に CPU キャッシュによるもので、これにより參照の空間的局所性が利用されます。

順次要素アクセスと非順次要素アクセスのパフォーマンスへの影響を示すために、MxN 行列 A から別の MxN 行列 B にすべての要素をコピーし、行列要素が列優(yōu)先で格納されていると仮定してコピーする次の関數(shù)を考えてみましょう。注文します。

# Import all of NumPy:
import numpy as np

# Define arrays:
x = np.asarray(...)
y = np.asarray(...)

# Perform operation:
y[::2,:,:] += 5.0 * x[::2,:,:]

A と B を次の 3x2 行列とします:

A=[123456]、 B=[00000 0]A = begin{bmatrix}1 & 2 \3 & 4 \5 & 6end{bmatrix}, B = begin{bmatrix}0 & 0 \0 & 0 \0 & 0end{bmatrix}A=?? 135 246 ? ?、B=? ?000 000??

A と B の両方が列優(yōu)先の順序で格納されている場(chǎng)合、次のようにコピー ルーチンを呼び出すことができます。

pip install numpy

ただし、A と B が両方とも行優(yōu)先の順序で格納されている場(chǎng)合、呼び出し署名は次のように変更されます。

# Import all of NumPy:
import numpy as np

# Define arrays:
x = np.asarray(...)
y = np.asarray(...)

# Perform operation:
y[::2,:,:] += 5.0 * x[::2,:,:]

後者のシナリオでは、da0 が 2、da1 が -5 であり、db0 と db1 についても同様であるため、最も內(nèi)側(cè)のループ內(nèi)の要素に順番にアクセスできないことに注意してください。代わりに、配列インデックスの「ポインター」は、ia = {0, 2, 4, 1, 3, 5} と ib が同じ場(chǎng)合に、線形メモリ內(nèi)の以前の要素に戻る前に繰り返し先へスキップします。図 3 は、非順次アクセスによるパフォーマンスへの影響を示しています。

LAPACK in your web browser

図 3: copy が列優(yōu)先の順序に従って要素に順次アクセスすることを想定している場(chǎng)合、正方列優(yōu)先行列と行優(yōu)先行列を copy に提供した場(chǎng)合のパフォーマンスの比較。 X 軸は、増加する行列のサイズ (つまり、要素の數(shù)) を列挙します。すべてのレートは、対応する行列サイズの列優(yōu)先の結(jié)果に対して正規(guī)化されます。

図から、要素?cái)?shù)が 1e5 (M = N = ~316) を超える正方行列を操作するまでは、列優(yōu)先のパフォーマンスと行優(yōu)先のパフォーマンスがほぼ同等であることがわかります。 1e6 要素 (M = N = ~1000) の場(chǎng)合、コピーする行優(yōu)先の行列を指定すると、パフォーマンスが 25% 以上低下します。 1e7 要素 (M = N = ~3160) では、85% を超えるパフォーマンスの低下が観察されます。パフォーマンスに大きな影響を與えるのは、行サイズが大きい行優(yōu)先の行列を操作する場(chǎng)合の參照の局所性の低下に起因する可能性があります。

LAPACK は Fortran で記述されているため、列優(yōu)先のアクセス順序を想定し、それに応じてアルゴリズムを?qū)g裝します。これは、行優(yōu)先の順序をサポートするだけでなく、それをデフォルトのメモリ レイアウトにする stdlib などのライブラリに問題を引き起こします。 LAPACK の Fortran 実裝を JavaScript に単純に移植した場(chǎng)合、行優(yōu)先の行列を提供するユーザーは、非順次アクセスに起因するパフォーマンスへの悪影響を経験することになります。

パフォーマンスへの悪影響を軽減するために、BLAS ルーチンで行優(yōu)先メモリ レイアウトと列優(yōu)先メモリ レイアウトの両方をサポートする BLAS に似たライブラリである BLIS からアイデアを借用し、ルーチンを Fortran から JavaScript に移植するときに修正された LAPACK 実裝を作成することにしました。 C では、次元ごとに個(gè)別のストライド パラメーターを使用して、列優(yōu)先と行優(yōu)先の両方のメモリ レイアウトに明示的に対応します。上記で定義したコピー関數(shù)に似た dlacpy などの一部の実裝では、別個(gè)の獨(dú)立したストライドを組み込むのは簡(jiǎn)単で、多くの場(chǎng)合、ストライド トリックやループ交換が必要になりますが、他の実裝では、次の理由により、変更がはるかに簡(jiǎn)単ではないことが判明しました。特殊な行列処理、さまざまなアクセス パターン、および組み合わせパラメータ化。

ndarray

LAPACK ルーチンは主に線形メモリに格納された行列を操作し、その要素は指定された次元と先頭 (つまり、最初の) 次元のストライドに従ってアクセスされます。次元は、それぞれの行と列の要素の數(shù)を指定します。ストライドは、行の次の要素にアクセスするためにリニア メモリ內(nèi)の要素をいくつスキップする必要があるかを指定します。 LAPACK は、同じ列に屬する要素が常に連続している (つまり、線形メモリ內(nèi)で隣接している) ことを前提としています。図 4 は、LAPACK 規(guī)約を視覚的に表したものです (具體的には、図 (a) および (b))。

LAPACK in your web browser

図 4: LAPACK ストライド配列規(guī)則を非連続ストライド配列に一般化した図。 a) 列優(yōu)先の順序で格納された 5 行 5 列の連続行列。 b) 列優(yōu)先の順序で格納された 3 行 3 列の不連続部分行列。 LAPACK では、最初のインデックス付き要素へのポインターを提供し、先頭 (つまり、最初の) 次元のストライドを指定することで部分行列を操作できます。この場(chǎng)合、列ごとに要素が 3 つしかないにもかかわらず、先頭次元のストライドは 5 になります。これは、より大きな行列の一部として格納された場(chǎng)合、線形メモリ內(nèi)のサブ行列要素が連続していないためです。 LAPACK では、後続 (つまり 2 番目) 次元のストライドは常に 1 であると想定されます。 c) 非単位ストライドを持ち、先頭次元と末尾次元の両方に LAPACK ストライド規(guī)約を一般化した、列優(yōu)先の順序で格納された 3 行 3 列の不連続部分行列。この一般化は、stdlib の多次元配列 (「ndarray」とも呼ばれます) を支えています。

NumPy や stdlib などのライブラリは、LAPACK のストライド配列規(guī)則を一般化してサポートします

  1. 最後の次元の非単位ストライド (図 4 (c) を參照)。 LAPACK は、行列の最後の次元が常にユニット ストライドを持つことを前提としています (つまり、列內(nèi)の要素は線形メモリに連続して格納されます)。
  2. どの次元でもマイナスのストライド。 LAPACK では、先頭の行列次元のストライドが正である必要があります。
  3. 3 つ以上の次元を持つ多次元配列。 LAPACK は、ストライド ベクトルと (部分) 行列のみを明示的にサポートします。

最後の次元での非ユニット ストライドのサポートにより、明示的なデータ移動(dòng)を必要とせずに、線形メモリの不連続ビューの O(1) 作成が確実にサポートされます。これらのビューは、多くの場(chǎng)合「スライス」と呼ばれます。例として、stdlib.
によって提供される API を使用してそのようなビューを作成する次のコード スニペットを考えてみましょう。

pip install numpy

最後の次元での非ユニット ストライドのサポートがなければ、選択した要素を新しい線形オブジェクトにコピーする必要があるため、式 x['::2,::2'] からビューを返すことはできません。連続性を確保するためにメモリバッファを使用します。

LAPACK in your web browser

図 5: ストライド操作を使用して線形メモリに格納された行列要素の反転および回転ビューを作成する様子を示す概略図。すべてのサブ回路図について、ストライドは [trailing_dimension, leading_dimension] としてリストされます。各回路図には暗黙的な「オフセット」があり、これは、行列 A の場(chǎng)合、要素 Aij が次のようになる、最初のインデックス付き要素のインデックスを示します。 i?strides[1] j?strides[0] オフセットに従って解決されます。 a) 列優(yōu)先の順序で格納された 3 行 3 列の行列を指定すると、先頭次元と末尾の次元のストライドを操作して、1 つ以上の軸に沿った行列要素が逆の順序でアクセスされるビューを作成できます。 b) 同様のストライド操作を使用して、線形メモリ內(nèi)の配置を基準(zhǔn)にして行列要素の回転ビューを作成できます。

負(fù)のストライドのサポートにより、1 つ以上の次元に沿った要素の O(1) 反転および回転が可能になります (図 5 を參照)。たとえば、行列を上から下、左から右に反転するには、ストライドを無効にするだけで済みます。前のコード スニペットを基にして、次のコード スニペットは 1 つ以上の軸を中心に要素を反転する方法を示します。

pip install numpy

負(fù)のストライドの議論には、線形メモリ內(nèi)の最初のインデックス付き要素のインデックスを示す「オフセット」パラメータの必要性が暗黙的に含まれています。ストライド多次元配列 A およびストライドのリスト s の場(chǎng)合、要素 Aij???n に対応するインデックス方程式に従って解くことができます

idx=オフセット ?s0 j?s1 n?sN?1textrm{idx} = textrm{offset} i cdot s_0 j cdot s_1 ldots n cdot s_{N-1}idx=オフセット 私?s0 j?s1 n?sN?1

ここで、N は配列の次元數(shù)であり、sk は k 番目のストライドに対応します。

負(fù)のストライドをサポートする BLAS および LAPACK ルーチン (ストライド ベクトルを操作する場(chǎng)合にのみサポートされるもの (たとえば、上記の daxpy を參照)) では、インデックス オフセットは、次のコード スニペットのようなロジックを使用して計(jì)算されます。

pip install numpy

ここで、M はベクトル要素の數(shù)です。これは、提供されたデータ ポインターがベクトルの線形メモリの先頭を指していることを暗黙的に前提としています。 C などのポインタをサポートする言語では、線形メモリの異なる領(lǐng)域で操作するために、関數(shù)呼び出しの前にポインタ演算を使用してポインタを調(diào)整するのが一般的です。これは、少なくとも 1 次元の場(chǎng)合には比較的安価で簡(jiǎn)単です。

たとえば、上で定義した c_daxpy に戻ると、ポインター演算を使用して、入出力配列の 11 番目と 16 番目の要素 (注: ゼロベースのインデックス付け) から始まる線形メモリ內(nèi)の 5 つの要素への要素アクセスを制限できます。次のコード スニペットに示すように、それぞれ。

# Import all of NumPy:
import numpy as np

# Define arrays:
x = np.asarray(...)
y = np.asarray(...)

# Perform operation:
y[::2,:,:] += 5.0 * x[::2,:,:]

ただし、JavaScript ではバイナリ バッファに対する明示的なポインタ演算をサポートしていないため、必要なバイト オフセットを持つ新しい型付き配列オブジェクトを明示的にインスタンス化する必要があります。次のコード スニペットでは、上記の C の例と同じ結(jié)果を得るために、型付き配列コンストラクターを解決し、新しいバイト オフセットを計(jì)算し、新しい型付き配列の長(zhǎng)さを計(jì)算して、新しい型付き配列インスタンスを作成する必要があります。

npm install @stdlib/ndarray-fancy @stdlib/blas-daxpy

配列サイズが大きい場(chǎng)合、型付き配列のインスタンス化のコストは、個(gè)々の配列要素へのアクセスと操作に費(fèi)やす時(shí)間と比較すると無視できます。ただし、配列サイズが小さい場(chǎng)合、オブジェクトのインスタンス化がパフォーマンスに大きな影響を與える可能性があります。

したがって、オブジェクトのインスタンス化のパフォーマンスへの悪影響を回避するために、stdlib は ndarray ビューの先頭に対応するバッファー要素の位置から ndarray のデータ バッファーを切り離します。これにより、次のコード スニペットに示すように、スライス式 x[2:,3:] および x[3:,1:] は、新しいバッファー インスタンスをインスタンス化する必要がなくせずに、新しい ndarray ビューを返すことができます。

// Individually import desired functionality:
import FancyArray from '@stdlib/ndarray-fancy';
import daxpy from '@stdlib/blas-daxpy';

// Define ndarray meta data:
const shape = [4, 4, 4];
const strides = [...];
const offset = 0;

// Define arrays using a "lower-level" fancy array constructor:
const x = new FancyArray('float64', [...], shape, strides, offset, 'row-major');
const y = new FancyArray('float64', [...], shape, strides, offset, 'row-major');

// Perform operation:
daxpy(5.0, x['::2,:,:'], y['::2,:,:']);

ndarray ビューの先頭からデータ バッファーを切り離した結(jié)果、同様に、ndarray データを使用して LAPACK ルーチンを呼び出すときに、新しい型付き配列インスタンスをインスタンス化する必要を回避しようとしました。これは、すべてのストライドされたベクトルと行列の明示的なオフセット パラメーターをサポートする、修正された LAPACK API シグネチャを作成することを意味します。

わかりやすくするために、上で定義した daxpy の JavaScript 実裝に戻りましょう。

pip install numpy

次のコード スニペットに示すように、最初のインデックス付き要素を解決する責(zé)任が API コンシューマーに移されるように、上記の署名と実裝を変更できます。

# Import all of NumPy:
import numpy as np

# Define arrays:
x = np.asarray(...)
y = np.asarray(...)

# Perform operation:
y[::2,:,:] += 5.0 * x[::2,:,:]

ndarray の場(chǎng)合、解決は ndarray のインスタンス化中に行われるため、ndarray データを使用した daxpy_ndarray の呼び出しは、関連付けられた ndarray メタ データを直接渡すことになります。これは、次のコード スニペットで示されています。

npm install @stdlib/ndarray-fancy @stdlib/blas-daxpy

BLIS と同様に、私たちは従來の LAPACK API シグネチャ (例: 下位互換性) と修正された API シグネチャ (例: パフォーマンスへの悪影響を最小限に抑えるため) の両方に価値があると考え、従來の LAPACK API シグネチャと修正された API シグネチャの両方を提供する計(jì)畫に落ち著きました。各 LAPACK ルーチンの API を変更しました。コードの重複を最小限に抑えるために、私たちは、上位レベルの API でラップできる共通の下位レベルの「ベース」実裝を?qū)g裝することを目指しました。上記の BLAS ルーチン daxpy の変更は比較的簡(jiǎn)単に見えるかもしれませんが、従來の LAPACK ルーチンとその期待される動(dòng)作の一般化された実裝への変換は、それほど簡(jiǎn)単ではないことがよくあります。

ドラスワップ

課題はもうたくさんです!最終的な製品はどのようなものになるでしょうか?!

一周して、これを dlswp に戻しましょう。これは、ピボット インデックスのリストに従って入力行列に対して一連の行交換を?qū)g行する LAPACK ルーチンです。次のコード スニペットは、リファレンス LAPACK Fortran 実裝を示しています。

// Individually import desired functionality:
import FancyArray from '@stdlib/ndarray-fancy';
import daxpy from '@stdlib/blas-daxpy';

// Define ndarray meta data:
const shape = [4, 4, 4];
const strides = [...];
const offset = 0;

// Define arrays using a "lower-level" fancy array constructor:
const x = new FancyArray('float64', [...], shape, strides, offset, 'row-major');
const y = new FancyArray('float64', [...], shape, strides, offset, 'row-major');

// Perform operation:
daxpy(5.0, x['::2,:,:'], y['::2,:,:']);

C からの Fortran 実裝とのインターフェイスを容易にするために、LAPACK は LAPACKE と呼ばれる 2 レベルの C インターフェイスを提供します。これは Fortran 実裝をラップし、行と列の両方の入力行列と出力行列に対応します。 dlswp の中間レベルのインターフェイスを次のコード スニペットに示します。

void c_daxpy(const int N, const double alpha, const double *X, const int strideX, double *Y, const int strideY) {
    int ix;
    int iy;
    int i;
    if (N <= 0) {
        return;
    }
    if (alpha == 0.0) {
        return;
    }
    if (strideX < 0) {
        ix = (1-N) * strideX;
    } else {
        ix = 0;
    }
    if (strideY < 0) {
        iy = (1-N) * strideY;
    } else {
        iy = 0;
    }
    for (i = 0; i < N; i++) {
        Y[iy] += alpha * X[ix];
        ix += strideX;
        iy += strideY;
    }
    return;
}

列優(yōu)先行列 a を指定して呼び出された場(chǎng)合、ラッパー LAPACKE_dlaswp_work は、指定された引數(shù)を Fortran 実裝に渡すだけです。ただし、行優(yōu)先行列 a を使用して呼び出された場(chǎng)合、ラッパーはメモリを割り當(dāng)て、明示的に a を転置して一時(shí)行列 a_t にコピーし、先頭の次元のストライドを再計(jì)算し、a_t で dlaswp を呼び出し、a_t に格納された結(jié)果を転置してコピーする必要があります。に変換し、最後に割り當(dāng)てられたメモリを解放します。これはかなりの作業(yè)量であり、ほとんどの LAPACK ルーチンで共通です。

次のコード スニペットは、JavaScript に移植された參照 LAPACK 実裝を示しており、先頭と末尾の次元ストライド、インデックス オフセット、ピボット インデックスを含むストライド ベクトルをサポートしています。

const binary = new UintArray([...]);

const mod = new WebAssembly.Module(binary);

従來の LAPACK と一貫した動(dòng)作を持つ API を提供するために、次のコード スニペットに示すように、上記の実裝をラップし、入力引數(shù)を「基本」実裝に適合させました。

pip install numpy

その後、私は別の同様のラッパーを作成しました。これは、stdlib の多次元配列に直接マッピングする API を提供し、次のコード スニペットに示すように、ピボットを適用する方向が負(fù)の場(chǎng)合に特別な処理を?qū)g行します。

# Import all of NumPy:
import numpy as np

# Define arrays:
x = np.asarray(...)
y = np.asarray(...)

# Perform operation:
y[::2,:,:] += 5.0 * x[::2,:,:]

いくつかの注意點(diǎn):

  1. 従來の LAPACKE API とは対照的に、順序は指定されたストライドから推測(cè)できるため、dlaswp_ndarray およびベース API では、matrix_layout (order) パラメーターは必要ありません。
  2. 従來の LAPACKE API とは対照的に、入力行列が行優(yōu)先の場(chǎng)合、データを一時(shí)ワークスペース配列にコピーする必要がないため、不必要なメモリ割り當(dāng)てが削減されます。
  3. BLAS や LAPACK と直接インターフェイスする NumPy や SciPy などのライブラリとは対照的に、stdlib で LAPACK ルーチンを呼び出す場(chǎng)合、事前に一時(shí)ワークスペース配列との間で非連続多次元データをコピーする必要はありません。と呼び出し後、それぞれ。ハードウェアに最適化された BLAS および LAPACK とインターフェイスする場(chǎng)合を除き、追求されたアプローチによりデータの移動(dòng)が最小限に抑えられ、リソースに制約のあるブラウザ アプリケーションでのパフォーマンスが保証されます。

OpenBLAS などのハードウェアに最適化されたライブラリを利用したいサーバー側(cè)アプリケーションの場(chǎng)合、一般化された署名引數(shù)を最適化された同等の API に適合させる個(gè)別のラッパーを提供します。このコンテキストでは、少なくとも十分に大きな配列の場(chǎng)合、一時(shí)コピーを作成することはオーバーヘッドの価値がある可能性があります。

現(xiàn)狀と次のステップ

課題、予期せぬ挫折、設(shè)計(jì)の複數(shù)回の反復(fù)にもかかわらず、上記の dlswp に加えて、さまざまな LAPACK ルーチンと関連ユーティリティのサポートを追加する 35 の PR を開くことができたことをうれしく報(bào)告します。明らかに 1,700 のルーチンには達(dá)していませんが、良いスタートです。 :)

それでも、未來は明るく、私たちはこの仕事にとても興奮しています。改善や追加の研究開発の余地はまだたくさんあります。特に私たちは

に熱心に取り組んでいます。
  1. ツールと自動(dòng)化について調(diào)べます。
  2. 複數(shù)の stdlib パッケージにまたがる Fortran 依存関係のソース ファイルを解決する際のビルドの問題に対処します。
  3. C および Fortran の実裝と、stdlib の既存の LAPACK パッケージのネイティブ バインディングを展開します。
  4. モジュラー LAPACK ルーチンの stdlib ライブラリを拡張し続けます。
  5. パフォーマンスを最適化するための追加領(lǐng)域を特定します。

Quansight Labs でのインターンシップは終了しましたが、私の計(jì)畫は引き続きパッケージを追加し、この取り組みを推進(jìn)することです。 LAPACK の計(jì)り知れない可能性と基本的な重要性を考慮すると、LAPACK を Web に導(dǎo)入するというこの取り組みが今後も成長(zhǎng)し続けることを願(yuàn)っています。そのため、この推進(jìn)を支援することに興味がある場(chǎng)合は、遠(yuǎn)慮なくご連絡(luò)ください。開発のスポンサーにご興味がございましたら、Quansight の擔(dān)當(dāng)者が喜んでお話しさせていただきます。

それでは、このインターンシップの機(jī)會(huì)を提供してくださったQuansightに感謝したいと思います。とても多くのことを?qū)Wべて本當(dāng)に幸運(yùn)だと感じています。クアンサイトでインターンになることは私の長(zhǎng)年の夢(mèng)であり、それを?qū)g現(xiàn)できたことにとても感謝しています。 Athan Reines と、素晴らしい指導(dǎo)者であり、すべてにおいて素晴らしい人である Melissa Mendon?a に特別な感謝の意を表したいと思います。そして、途中で大なり小なり私を助けてくれたすべての stdlib コア開発者と Quansight の他の全員に感謝します。

乾杯!


stdlib は、プロジェクトの開発を加速し、専門家が作成した高品質(zhì)のソフトウェアに依存しているという安心感を與える、堅(jiān)牢で高性能のライブラリの包括的なスイートを提供することに特化したオープン ソース ソフトウェア プロジェクトです。

この投稿が気に入ったら、スターを付けてください。 GitHub でプロジェクトを財(cái)政的に支援することを検討してください。あなたの貢獻(xiàn)と継続的なサポートは、プロジェクトの長(zhǎng)期的な成功を確実にするのに役立ち、非常に感謝しています!

以上がWebブラウザのLAPACKの詳細(xì)內(nèi)容です。詳細(xì)については、PHP 中國(guó)語 Web サイトの他の関連記事を參照してください。

このウェブサイトの聲明
この記事の內(nèi)容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰屬します。このサイトは、それに相當(dāng)する法的責(zé)任を負(fù)いません。盜作または侵害の疑いのあるコンテンツを見つけた場(chǎng)合は、admin@php.cn までご連絡(luò)ください。

ホットAIツール

Undress AI Tool

Undress AI Tool

脫衣畫像を無料で

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード寫真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

寫真から衣服を削除するオンライン AI ツール。

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡(jiǎn)単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中國(guó)語版

SublimeText3 中國(guó)語版

中國(guó)語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強(qiáng)力な PHP 統(tǒng)合開発環(huán)境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Java vs. JavaScript:混亂を解消します Java vs. JavaScript:混亂を解消します Jun 20, 2025 am 12:27 AM

JavaとJavaScriptは異なるプログラミング言語であり、それぞれ異なるアプリケーションシナリオに適しています。 Javaは大規(guī)模なエンタープライズおよびモバイルアプリケーション開発に使用されますが、JavaScriptは主にWebページ開発に使用されます。

JavaScriptコメント:短い説明 JavaScriptコメント:短い説明 Jun 19, 2025 am 12:40 AM

JavaScriptcommentsEareEssentialential-formaining、およびGuidingCodeexecution.1)single-linecommentseared forquickexplanations.2)多LinecommentsexplaincomplexlogiCorprovidededocumentation.3)clarifyspartsofcode.bestpractic

JSで日付と時(shí)間を操作する方法は? JSで日付と時(shí)間を操作する方法は? Jul 01, 2025 am 01:27 AM

JavaScriptで日付と時(shí)間を処理する場(chǎng)合は、次の點(diǎn)に注意する必要があります。1。日付オブジェクトを作成するには多くの方法があります。 ISO形式の文字列を使用して、互換性を確保することをお?jiǎng)幛幛筏蓼埂?2。時(shí)間情報(bào)を取得および設(shè)定して、メソッドを設(shè)定でき、月は0から始まることに注意してください。 3.手動(dòng)でのフォーマット日付には文字列が必要であり、サードパーティライブラリも使用できます。 4.ルクソンなどのタイムゾーンをサポートするライブラリを使用することをお?jiǎng)幛幛筏蓼?。これらの重要なポイントを?xí)得すると、一般的な間違いを効果的に回避できます。

なぜの下部にタグを配置する必要があるのですか? なぜの下部にタグを配置する必要があるのですか? Jul 02, 2025 am 01:22 AM

PLACSTHETTHETTHE BOTTOMOFABLOGPOSTORWEBPAGESERVESPAGESPORCICALPURPOSESESFORSEO、userexperience、andDesign.1.IthelpswithiobyAllowingseNStoAccessKeysword-relevanttagwithtagwithtagwithtagwithemaincontent.2.iTimrovesexperiencebyepingepintepepinedeeping

JavaScript vs. Java:開発者向けの包括的な比較 JavaScript vs. Java:開発者向けの包括的な比較 Jun 20, 2025 am 12:21 AM

javascriptispreferredforwebdevelopment、whilejavaisbetterforlge-scalebackendsystemsandroidapps.1)javascriptexcelsininintingtivewebexperiences withitsdynAmicnature anddommanipulation.2)javaofferstruntypyping-dobject-reientedpeatures

JavaScript:効率的なコーディングのためのデータ型の調(diào)査 JavaScript:効率的なコーディングのためのデータ型の調(diào)査 Jun 20, 2025 am 12:46 AM

javascripthassevenfundamentaldatypes:number、string、boolean、undefined、null、object、andsymbol.1)numberseadouble-precisionformat、有用であるため、有用性の高いものであるため、but-for-loating-pointarithmetic.2)ストリングリムムット、使用率が有用であること

DOMでのイベントの泡立ちとキャプチャとは何ですか? DOMでのイベントの泡立ちとキャプチャとは何ですか? Jul 02, 2025 am 01:19 AM

イベントキャプチャとバブルは、DOMのイベント伝播の2つの段階です。キャプチャは最上層からターゲット要素までであり、バブルはターゲット要素から上層までです。 1.イベントキャプチャは、AddEventListenerのUseCaptureパラメーターをTrueに設(shè)定することにより実裝されます。 2。イベントバブルはデフォルトの動(dòng)作であり、UseCaptureはfalseに設(shè)定されているか、省略されます。 3。イベントの伝播を使用して、イベントの伝播を防ぐことができます。 4.イベントバブルは、動(dòng)的なコンテンツ処理効率を改善するためにイベント委任をサポートします。 5.キャプチャを使用して、ロギングやエラー処理など、事前にイベントを傍受できます。これらの2つのフェーズを理解することは、タイミングとJavaScriptがユーザー操作にどのように反応するかを正確に制御するのに役立ちます。

JavaScriptアプリケーションのペイロードサイズをどのように削減できますか? JavaScriptアプリケーションのペイロードサイズをどのように削減できますか? Jun 26, 2025 am 12:54 AM

JavaScriptアプリケーションがゆっくりとロードされ、パフォーマンスが低い場(chǎng)合、問題はペイロードが大きすぎることです。ソリューションには、次のものが含まれます。1。コード分割(コードスプリッティング)を使用し、React.lazy()またはビルドツールを介して大きなバンドルを複數(shù)の小さなファイルに分割し、最初のダウンロードを減らすために必要に応じてロードします。 2。未使用のコード(Treeshaking)を削除し、ES6モジュールメカニズムを使用して「デッドコード」をクリアして、導(dǎo)入されたライブラリがこの機(jī)能をサポートしていることを確認(rèn)します。 3.リソースファイルを圧縮してマージし、GZIP/BrotliとTerserがJSを圧縮できるようにし、ファイルを合理的にマージし、靜的リソースを最適化します。 4.頑丈な依存関係を交換し、day.jsやフェッチなどの軽量ライブラリを選択します

See all articles