.. 9 Drivers ======== ドライバ ======== .. This chapter provides a (very) brief overview on how to write efficient drivers. It is assumed that you already have a good understanding of drivers. この章では、効率のよいドライバを書くための短い概要を紹介します。この章は、すでにドライバを良く理解している人を対象としています。 .. 9.1 Drivers and concurrency ドライバと並行性 ================ .. The run-time system will always take a lock before running any code in a driver. ランタイムシステムはドライバ内のコードを実行する前はいつも、何かしらのロックをかけます。 .. By default, that lock will be at the driver level, meaning that if several ports has been opened to the same driver, only code for one port at the same time can be running. デフォルトでは、ドライバのレベルでロックがかけられます。そのため、1つのドライバでいくつかのポートを開いていたとすると、同時に2つ以上のポートに対する処理を実行することはできません。 .. A driver can be configured to instead have one lock for each port. それぞれのポートごとにロックをかけられるように、設定することもできます。 .. If a driver is used in a functional way (i.e. it holds no state, but only does some heavy calculation and returns a result), several ports with registered names can be opened beforehand and the port to be used can be chosen based on the scheduler ID like this: もしドライバが関数的に使われる(状態をもたず、重い計算を行って返すだけ)の場合には、あらかじめポートに名前をつけてオープンしておいて、次のようなスケジュールIDに基づいてポートが選ばれるようにすることができます。 .. code-block:: erlang -define(PORT_NAMES(), {some_driver_01, some_driver_02, some_driver_03, some_driver_04, some_driver_05, some_driver_06, some_driver_07, some_driver_08, some_driver_09, some_driver_10, some_driver_11, some_driver_12, some_driver_13, some_driver_14, some_driver_15, some_driver_16}). client_port() -> element(erlang:system_info(scheduler_id) rem tuple_size(?PORT_NAMES()) + 1, ?PORT_NAMES()). .. As long as there are no more than 16 schedulers, there will never be any lock contention on the port lock for the driver. この場合は、16以上のスケジューラがない限りは、ドライバに対するポートのロックは行われません。 .. 9.2 Avoiding copying of binaries when calling a driver ドライバ呼び出し時に巨大なバイナリのコピーを避ける ================================================== .. There are basically two ways to avoid copying a binary that is sent to a driver. バイナリ呼び出し時にバイナリのコピーを避ける方法は2つあります。 .. If the Data argument for port_control/3 is a binary, the driver will be passed a pointer to the contents of the binary and the binary will not be copied. If the Data argument is an iolist (list of binaries and lists), all binaries in the iolist will be copied. もし、 :func:`port_control/3` の ``Data`` 引き数がバイナリであった場合、ドライバはバイナリの内容のを指すポイントを渡すため、バイナリのコピーは行われません。もし、 ``Data`` 引き数が ``iolist`` であった場合には(バイナリのリストとリスト)、 ``iolist`` 内のすべてのバイナリはコピーされます。 .. Therefore, if you want to send both a pre-existing binary and some additional data to a driver without copying the binary, you must call port_control/3 twice; once with the binary and once with the additional data. However, that will only work if there is only one process communicating with the port (because otherwise another process could call the driver in-between the calls). そのため、もし既存のバイナリと追加のデータを、コピーせずに一緒に渡したいと思うのであれば、 :func:`port_control/3` を2回呼び出す必要があります。しかし、この方法は1つのプロセスだけがそのポートにアクセスしているときしか正常に動作しません。その2回の呼び出しに間に他のプロセスが呼び出しをするかもしれないからです。 .. Another way to avoid copying binaries is to implement an outputv callback (instead of an output callback) in the driver. If a driver has an outputv callback, refc binaries passed in an iolist in the Data argument for port_command/2 will be passed as references to the driver. バイナリのコピーを防ぐもう1つの方法は、 :func:`output` コールバックの代わりに、 :func:`outputv` コールバックを実装する方法です。もしドライバが :func:`outputv` コールバックを実装していれば、 :func:`port_command/2` の ``Data`` 引き数に ``iolist`` に渡されて、その中にrefcバイナリが含まれていた場合、参照だけが渡されます。 .. 9.3 Returning small binaries from a driver ドライバから小さなバイナリを返す ================================ .. The run-time system can represent binaries up to 64 bytes as heap binaries. They will always be copied when sent in a messages, but they will require less memory if they are not sent to another process and garbage collection is cheaper. ランタイムシステムは、64バイトまでのバイナリは、ヒープバイナリとして扱うことができます。メッセージに載せて送られる時は、常にコピーされます。送られない場合よりもメモリ消費は負えますが、ガベージコレクションは軽くなります。 .. If you know that the binaries you return are always small, you should use driver API calls that do not require a pre-allocated binary, for instance driver_output() or driver_output_term() using the ERL_DRV_BUF2BINARY format, to allow the run-time to construct a heap binary. もし返ってくるバイナリがいつも小さいということが分かっているのであれば、事前にアロケートされたバイナリを必要としないAPI呼び出しを使用すべきでしょう。 :c:func:`driver_output()` の代わりに、 実行時にヒープバイナリを作れる、 ``ERL_DRV_BUF2BINARY`` フォーマットを指定した :c:func:`driver_output_term()` を使ってください。 .. 9.4 Returning big binaries without copying from a driver ドライバからコピーしないで巨大なバイナリを返す ============================================== .. To avoid copying data when a big binary is sent or returned from the driver to an Erlang process, the driver must first allocate the binary and then send it to an Erlang process in some way. ドライバ側から、巨大なバイナリをErlangのプロセスに返すときに、データのコピーを避けたいのであれば、ドライバー側でバイナリのアロケートを行い、それを何らかの方法でErlangプロセスに送る必要があります。 .. Use driver_alloc_binary() to allocate a binary. バイナリの割り当てには、 ``driver_alloc_binary()`` を使用してください。 .. There are several ways to send a binary created with driver_alloc_binary(). ``driver_alloc_binary()`` を使って作られたバイナリの送信方法には何通りかあります。 .. From the control callback, a binary can be returned provided that set_port_control() has been called with the flag value PORT_CONTROL_FLAG_BINARY. ``PORT_CONTROL_FLAG_BINARY`` を付けて ``set_port_control()`` が制御コールバックから呼ばれていると、バイナリをかえすことができます。 .. A single binary can be sent with driver_output_binary(). 一つののバイナリは、 ``driver_output_binary()`` を使って送信することができます。 .. Using driver_output_term() or driver_send_term(), a binary can be included in an Erlang term. ``driver_output_term()`` 、もしくは ``driver_send_term()`` を使うと、Erlangの項にバイナリを含めることができます。