2

C NIF が多くの Erlang プロセスによって同時に呼び出されたときに、それらのブロック動作を観察しました。ノンブロッキングにすることはできますか?私がmutex理解できないことがここで働いていますか?

PS基本的な「Hello world」NIFは、特定の呼び出しの場合にsleep100を作成することでテストできます。NIF を呼び出している他の PID は、スリープが実行されるのを待ってから実行されることがわかります。microsecondsPID

非ブロッキング動作は、並行性が問題を引き起こさない可能性がある場合 (配列のプッシュ、カウンターのインクリメントなど) に役立ちます。

spawnerconc_nif_callerおよびniftestモジュールでそれぞれ構成される 4 つの要点へのリンクを共有しています。の値をいじってみましたが、Val実際に非ブロッキング動作を観察しました。これは、関数に大きな整数パラメーターを割り当てることによって確認されspawn_multiple_nif_callersます。

spawner.erl 、conc_nif_caller.erlniftest.erl、最後にniftest.cをリンクします。

以下の行は、私の Mac の Erlang REPL によって出力されます。

Erlang/OTP 17 [erts-6.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
4

3 に答える 3

5

NIF 自体にはミューテックスはありません。C で 1 つ実装でき、NIF のオブジェクトをロードするときに 1 つ存在しますが、これはモジュールをロードするときに 1 回だけ実行する必要があります。

起こっている可能性のあることの 1 つ (そして、それが起こっているに違いないと思います) は、C コードが Erlang スケジューラーを台無しにすることです。

戻る前に長い作業を行うネイティブ関数は、VM の応答性を低下させ、さまざまな奇妙な動作を引き起こす可能性があります。このような異常な動作には、過度のメモリ使用や、スケジューラ間の不適切な負荷分散が含まれますが、これらに限定されません。長時間の作業が原因で発生する可能性のある奇妙な動作も、OTP リリース間で異なる場合があります。

また、その意味と解決方法の説明。lengty work

ほんの少しの言葉で(かなり単純化して):

コア用に 1 つのスケジューラーが作成されます。それぞれが実行できるプロセスのリストを持っています。あるスケジューラーリストが空の場合、彼は別のスケジューラーリストから作業を続けようとします。静止するものが何もない (または十分でない) 場合、これは失敗する可能性があります。

Erlang スケジューラは、あるプロセスである程度の作業を行い、別のプロセスに移動し、そこである程度の作業を行い、別のプロセスに移動します。などなど。これは、システム プロセスでのスケジューリングに非常に似ています。

ここで非常に重要なことの 1 つは、作業量の計算です。デフォルトでは、各関数呼び出しにはいくつかのリダクションが割り当てられています。追加には 2 つ、モジュール内の呼び出し関数には 1 つ、メッセージの送信にも 1 つ、一部のビルドインには複数の ( のようにlist_to_binary) が含まれる可能性があります。2000 の削減を収集すると、別のプロセスに移動します。

では、C 関数のコストはいくらですか? 1回だけ減額です。

コードのような

loop() ->
   call_nif_function(),
   loop().

丸 1 時間かかる可能性がありますが、スケジューラはこの 1 つのプロセスでスタックします。これは、まだ 2000 の削減をカウントしていないためです。または、言い換えれば、彼は前進する可能性なしにNIF内で立ち往生する可能性があります(少なくともすぐに)。

これを回避する方法はいくつかありますが、一般的なルールとして、stat NIF には時間がかかりません。したがって、C コードを長時間実行している場合は、代わりにドライバーを使用する必要があります。それらは、実装と管理がはるかに簡単で、NIF をいじる必要があります。

于 2014-10-23T18:09:35.300 に答える
4

NIF 呼び出しは、それらを呼び出したプロセスがバインドされているスケジューラーをブロックします。したがって、あなたの例では、他のプロセスが同じスケジューラー上にある場合、最初のプロセスが終了するまで NIF を呼び出すことはできません。

この点で、NIF 呼び出しを非ブロッキングにすることはできません。ただし、独自のスレッドを生成して、作業の負荷をスレッドにオフロードすることはできます。

そのようなスレッドは、ローカルのErlang プロセス (同じマシン上のプロセス) にメッセージを送信できます。そのため、生成されたスレッドがメッセージを送り返すのを待つことで、必要な応答を得ることができます。

悪い例:

static ERL_NIF_TERM my_function(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    MyStruct* args = new MyStruct(); // I like C++; so sue me
    args->caller = enif_self();
    ErlNifTid thread_id;
    // Please remember, you must at some point rejoin the thread, 
    // so keep track of the thread_id
    enif_thread_create("my_function_thread", &thread_id, my_worker_function, (void*)args, NULL);
    return enif_make_atom(env, "ok");
}
void* my_worker_function(void* args) {
    sleep(100);
    ErlNifEnv* msg_env = enif_alloc_env();
    ERL_NIF_TERM msg = enif_make_atom(msg_env, "ok");
    enif_send(NULL, args->caller, msg_env, msg);
    delete args;
    return NULL;
}

そしてあなたのerlangソースで:

test_nif() -> 
    my_nif:my_function(),
    receive
        ok -> ok
    end.

とにかく、その趣旨の何か。

于 2014-10-23T18:03:20.637 に答える