5

私はAndroidアプリケーション(明らかにJavaで)に取り組んでおり、最近UDPリーダーコードを更新しました。両方のバージョンで、いくつかのバッファーをセットアップし、UDP パケットを受信します。

byte[] buf = new byte[10000];
short[] soundData = new short[1000];
DatagramPacket packet = new DatagramPacket (buf, buf.length);
socket.receive (packet);

最初のバージョンでは、一度に 1 バイトずつデータをまとめました (実際には 16 個の PCM オーディオ データです)。

for (int i = 0; i < count; i++)
    soundData[i] = (short) (((buf[k++]&0xff) << 8) + (buf[k++]&0xff));

更新されたバージョンでは、私が始めたときには知らなかったいくつかのクールな Java ツールを使用しました。

bBuffer  = ByteBuffer.wrap (buf);
sBuffer  = bBuffer.asShortBuffer();
sBuffer.get (soundData, 0, count);

どちらの場合も、「カウント」は正しく設定されています (確認しました)。しかし、オーディオのストリーミングに新たな問題が発生しているようです。おそらく処理速度が十分ではありません。私には意味がありません。明らかに、バッファ コードは JVM コードの 3 つ以上のステートメントにコンパイルされますが、これを開始したとき、2 番目のバージョンは 1 番目よりも高速であるという合理的な仮定のように思えました。

明らかに、私は自分のコードが Java NIO バッファを使用しなければならないと主張しているわけではありませんが、少なくとも一見したところ、これを実行するのは「ベタ」のように思えます。

高速でシンプルな Java UDP リーダーの推奨事項と、一般的に受け入れられている「最善の方法」があるかどうかを誰かが知りましたか??

ありがとう、R.

4

3 に答える 3

4

パケットをバイト配列に読み込んで(ネイティブバッファーから配列にデータをコピーして)、新しいByteBufferでラップして(新しいオブジェクトを作成して)、ShortBufferに変換して(新しいオブジェクト)オブジェクトを1回だけ設定し、コピーを回避しました。

これを行うには、DatagramChannel.socket()を使用してソケットを作成し、通常どおりに接続し、socket.getChannel()を使用してDatagramChannelオブジェクトを取得します。このオブジェクトを使用すると、既存のByteBufferにパケットを直接読み込むことができます(効率を最大化するには、ByteBuffer.allocateDirectを使用して作成する必要があります)。次に、asShortBuffer()を1回だけ使用して、データのビューをshortとして作成し、ByteBufferを補充するたびにそのShortBufferから読み取ることができます。

したがって、コードは次のようになります。

 DatagramSocket socket = DatagramChannel.socket();
 // code to connect socket
 DatagramChannel channel = socket.getChannel();
 ByteBuffer buffer = ByteBuffer.allocateDirect (10000);
 // you may want to invoke buffer.order(...) here to tell it what byte order to use
 ShortBuffer shortBuf = buffer.asShortBuffer();

 // in your receive loop:
 buffer.clear();
 channel.receive(buffer);
 shortBuf.position(0).limit(buffer.position()/2); // may ignore a byte if odd number received
 shortBuf.get(soundBuf,0,shortBuf.limit());

これは、データのコピー全体を回避し、フォーマット変換が、最適ではない可能性のあるコンパイラ生成のバイト操作ではなく、手動で最適化されたコードによって処理されるため、以前のコードよりもはるかに効率的であることがわかります。プラットフォームネイティブのバイト順序を使用すると、いくらか効率的になります(Androidは、使用可能なすべてのプラットフォームでリトルエンディアンのバイト順序を使用していると思います。上記のコードはビッグエンディアンのようであるため、これは不可能な場合があります。この場合、shortBuf.get()はダイレクトメモリコピーになります。

于 2012-05-11T08:28:29.553 に答える
2

一般に、プリミティブ型を直接操作する方が、オブジェクトや関数呼び出しなどの作成のオーバーヘッドを回避できるため、オブジェクトを操作するよりも効率的です。

速度以外のユーティリティオブジェクトを使用する理由があります:利便性、安全性など。

この特定のケースで違いをテストする最良の方法は、実際にそれを測定することです。大きなデータセットで両方の方法を試して、時間を計ってください。次に、この場合のメリットに見合う価値があるかどうかを判断できます。

Androidのプロファイラーを使用して、問題が実際にどこにあるかを確認することもできます。TraceViewを参照してください。

于 2010-10-28T00:43:47.437 に答える
0

このタスクには、バイト配列をラップした ByteArrayInputStream をラップした DataInputStream を使用します。ByteBuffer のオーバーヘッドなしで readShort() のシフトをカプセル化します。

または、DatagramPacket と DatagramSocket を使用するのではなく、DatagramChannel を介して DirectByteBuffer に直接読み込むこともできます。現在、java.net と java.nio を組み合わせて使用​​しています。

これら 2 つのアプローチに大きなパフォーマンスの違いはないと思いますが、どちらもハイブリッド アプローチよりも高速になると思います。

于 2010-10-29T02:29:29.727 に答える