C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

バイト配列とShort配列のやり取りに関して /Yammy (21/01/07(Thu) 21:39) #96662
Re[1]: バイト配列とShort配列のやり取りに関して /Hongliang (21/01/07(Thu) 22:07) #96664
Re[1]: バイト配列とShort配列のやり取りに関して /魔界の仮面弁士 (21/01/07(Thu) 22:03) #96663
  └ Re[2]: バイト配列とShort配列のやり取りに関して /Yammy (21/01/07(Thu) 23:12) #96665
    └ Re[3]: バイト配列とShort配列のやり取りに関して /とっちゃん (21/01/08(Fri) 01:12) #96666
      ├ Re[4]: バイト配列とShort配列のやり取りに関して /とっちゃん (21/01/08(Fri) 01:12) #96667
      └ Re[4]: バイト配列とShort配列のやり取りに関して /とっちゃん (21/01/08(Fri) 14:45) #96677
        └ Re[5]: バイト配列とShort配列のやり取りに関して /Yammy (21/01/08(Fri) 18:19) #96686
          └ Re[6]: バイト配列とShort配列のやり取りに関して /Hongliang (21/01/08(Fri) 18:39) #96687
            └ Re[7]: バイト配列とShort配列のやり取りに関して /とっちゃん (21/01/08(Fri) 22:15) #96690
              └ Re[8]: バイト配列とShort配列のやり取りに関して /Yammy (21/01/17(Sun) 17:51) #96731
                └ Re[9]: バイト配列とShort配列のやり取りに関して /とっちゃん (21/01/17(Sun) 20:49) #96732


親記事 / ▼[ 96664 ] ▼[ 96663 ]
■96662 / 親階層)  バイト配列とShort配列のやり取りに関して
□投稿者/ Yammy (1回)-(2021/01/07(Thu) 21:39:56)

分類:[.NET 全般] 

VB.NET2015に関する質問です。


あるバイト配列があり、
それの100バイトから後ろが全てShortデータになっているとします。
そのShortデータを0.3倍して、元のバイト配列に戻したいのですが
どのようにするのがもっとも効率が良いですか?



                    Dim buf(99999) As Byte 
                    Dim br = New BinaryReader(New MemoryStream(buf), Encoding.ASCII)


                    br.BaseStream.Seek(100, SeekOrigin.Begin)



                    For i = 1 To DataSize

                        Dim hh = br.ReadInt16 * 0.3

                    Next


のようにすれば良いかと思ったのですが、
得られた値のhhをバイト配列に戻すにはどのようにしたら良いですか?

あるいは、Forループを使うよりも

                    '    Dim intArray(16) As Integer
                    '  Dim byteArray() As Byte = intArray.Select(AddressOf Convert.ToByte).ToArray()

のようなSelectを使った方が高速なのでしょうか?

もっと良い方法があればご教授下さい。

[ □ Tree ] 返信 編集キー/

▲[ 96662 ] / 返信無し
■96664 / 1階層)  Re[1]: バイト配列とShort配列のやり取りに関して
□投稿者/ Hongliang (1141回)-(2021/01/07(Thu) 22:07:19)
どの視点における効率なのかという話になりますが。
まあこれぐらいなら多分どんな手段を取っても大差ないでしょうね、多分。

Dim br = New BinaryReader(New MemoryStream(buf, 100, buf.Length - 100))
Dim bw = New BinaryWriter(New MemoryStream(buf, 100, buf.Length - 100))
とやって、ループで
bw.Write(CShort(br.ReadInt16() * 0.3))
やっていくというのはどうでしょうか。
[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96662 ] / ▼[ 96665 ]
■96663 / 1階層)  Re[1]: バイト配列とShort配列のやり取りに関して
□投稿者/ 魔界の仮面弁士 (2945回)-(2021/01/07(Thu) 22:03:23)
2021/01/07(Thu) 22:05:49 編集(投稿者)

No96662 (Yammy さん) に返信
> 得られた値のhhをバイト配列に戻すにはどのようにしたら良いですか?

BitConverter クラスを使うとか
あるいは BinaryWriter とか…
[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96663 ] / ▼[ 96666 ]
■96665 / 2階層)  Re[2]: バイト配列とShort配列のやり取りに関して
□投稿者/ Yammy (2回)-(2021/01/07(Thu) 23:12:56)
ありがとうございます。

Hongliang さんの方法でうまくいきました。

ただ、一つ気になるのは、
バイト配列とバイト配列の間でのやり取り

buf(i) = CByte(buf(i) * Gain)

bw.Write(CByte(br.ReadByte * Gain))

を比較したところ、結果は同じなのですが、
前者の方が計算時間が半分で済みました。

どうもReaderWriterは配列そのものを扱うよりもオーバーヘッドが大きいようです。

そのため、できればReaderWriterを使わない方法も試してみたいのですが、
どのようにすれば更に高速なものができると思いますか?

BitConverter.ToInt16
で変換できることはもちろん知っていますが、
バイト配列からShortに変換して
また、同じバイト配列に戻すにはどうしたら良いですか?

BitConverter.GetBytes()
でバイト配列を取得後に
hh(100)=hh2(0)
hh(101)=hh2(1)
のようにして代入するしかないですか?

もっとスマートな方法があればご教授下さい。


[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96665 ] / ▼[ 96667 ] ▼[ 96677 ]
■96666 / 3階層)  Re[3]: バイト配列とShort配列のやり取りに関して
□投稿者/ とっちゃん (707回)-(2021/01/08(Fri) 01:12:12)
No96665 (Yammy さん) に返信

速度面も考慮しつつ、VB で限界までとすると以下のような格好ですかね?
(C# -> VB の変換のほうが時間がかかったのは内緒w)
GetBytes() でやってることとほぼ同じことを直接行ってるだけですけどね。

Dim buf(99999) As Byte

Dim count As Integer = CInt((buf.Length - 100) / 2)

For index As Integer = 0 To count - 1
Dim pos As Integer = 100 + index * 2
Dim value As Short = BitConverter.ToInt16(buf, pos)
value = CShort(value * 0.3)
' .NET Framework 限定
' Dim newData As Byte() = BitConverter.GetBytes(value)
' Array.Copy(newData, 0, buf, pos, newData.Length)
#if BIGENDIAN
buf(pos) = CByte((value Mod 256))
buf(pos+1) = CByte((value / 256))
#else
buf(pos) = CByte((value / 256))
buf(pos + 1) = CByte((value Mod 256))
#endif
Next

[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96666 ] / 返信無し
■96667 / 4階層)  Re[4]: バイト配列とShort配列のやり取りに関して
□投稿者/ とっちゃん (708回)-(2021/01/08(Fri) 01:12:48)
あ、図表モードになってない…orz
[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96666 ] / ▼[ 96686 ]
■96677 / 4階層)  Re[4]: バイト配列とShort配列のやり取りに関して
□投稿者/ とっちゃん (709回)-(2021/01/08(Fri) 14:45:02)
No96666 (とっちゃん さん) に返信
さっきふと見直してたところ、エンディアンの展開が逆になってました。
実際はこんな感じ。

リトルエンディアンだと、下位バイトから埋めていくので、大きな数値も賄う場合はループを回す方法もあります(コメントアウト部分)。

#If BIGENDIAN Then
  buf(pos) = CByte(value/256)
  buf(pos+1) = CByte(value Mod 256)
#Else
  buf(pos) = CByte(value Mod 256)
  buf(pos + 1) = CByte(value / 256)
  'For cpycnt = 1 To 2
  '    buf(pos + cpycnt) = CByte(value Mod 256)
  '    value = CShort(value / 256)
  'Next
#End If

[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96677 ] / ▼[ 96687 ]
■96686 / 5階層)  Re[5]: バイト配列とShort配列のやり取りに関して
□投稿者/ Yammy (3回)-(2021/01/08(Fri) 18:19:13)
ありがとうございます。


Int16形式に関しても、
ReaderWriter
BitConverter.ToInt16とBitConverter.GetBytes
buf(pos) = CByte((value / 256))

の3種類の方法を比較してみました。

その結果、最初の二つはほぼ同じ速度なのに対し、
3つめは2倍弱程度高速な結果が得られました。

見た目は古くさいし、読みづらいですが、
地道に変換するのがもっとも高速であることが分かりました。


ちなみに、

#if BIGENDIAN

#else

#endif

というのは何を表していますか?
私の環境だとBIGENDIAN=Trueの方がグレーアウトされてしまうのですが、
https://qiita.com/longlongago_k/items/955cccfc70c3e36d76a2
このページなど見ますとVSの場合にはリトルエンディアンしか使えないのではないでしょうか?
どういう場合にビッグエンディアンが有効になりますか?

[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96686 ] / ▼[ 96690 ]
■96687 / 6階層)  Re[6]: バイト配列とShort配列のやり取りに関して
□投稿者/ Hongliang (1143回)-(2021/01/08(Fri) 18:39:59)
> というのは何を表していますか?
> 私の環境だとBIGENDIAN=Trueの方がグレーアウトされてしまうのですが、

・プロジェクトのプロパティのビルド>条件付きコンパイルシンボルに BIGENDIANG を追加する
・ソース上のどこかで #define BIGENDIAN を記述する
のいずれかで、ビッグエンディアンとしてバイト配列から読み書きできるようになります。

> https://qiita.com/longlongago_k/items/955cccfc70c3e36d76a2
> このページなど見ますとVSの場合にはリトルエンディアンしか使えないのではないでしょうか?

Visual Studioではなくて、.NETのBitConverterがリトルエンディアンの変換しかサポートしていないという記事ですね。
その記事でいう「したがって、自分で実装しないといけない。」がとっちゃんさんのコードです。
[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96687 ] / ▼[ 96731 ]
■96690 / 7階層)  Re[7]: バイト配列とShort配列のやり取りに関して
□投稿者/ とっちゃん (710回)-(2021/01/08(Fri) 22:15:48)
No96686 (Yammy さん) に返信

あえてこちらに…

> その結果、最初の二つはほぼ同じ速度なのに対し、
> 3つめは2倍弱程度高速な結果が得られました。
>
JIT分は除外していますか?
具体的には起動後最初の1回は、JITが動くのでその分を排除して
時間差を求めないと求める答えは出にくいかもしれません。


> Int16形式に関しても、
> ReaderWriter
> BitConverter.ToInt16とBitConverter.GetBytes
> buf(pos) = CByte((value / 256))
>
> の3種類の方法を比較してみました。
>
> その結果、最初の二つはほぼ同じ速度なのに対し、
> 3つめは2倍弱程度高速な結果が得られました。
>
BitConverter は、内部的にはバイト操作だけですが
エラーチェックとかいくつかあるので、そういう処理がない直接操作に比べたら遅くなります。

こればっかりはどうしようもない部分ですね。

> 見た目は古くさいし、読みづらいですが、
> 地道に変換するのがもっとも高速であることが分かりました。
>
速度面で見ると、CShort( value * 0.3 ) は、浮動小数点演算になるので速度面で無駄が大きいです。
単純に、0.3 倍(元の30パーセント)するだけで、端数切捨てでいいのであれば
整数演算にすることでかなり速度アップが期待できます。


それ以外の速度アップとしては並列化(Parallel.Forなど)もあります。

並列化は整数演算と違ってどうしてもオーバーヘッドがあるのでどこまで早くなるかはわかりません。

>
> ちなみに、
>
> #if BIGENDIAN
>
> #else
>
> #endif
>
> というのは何を表していますか?
> 私の環境だとBIGENDIAN=Trueの方がグレーアウトされてしまうのですが、
> https://qiita.com/longlongago_k/items/955cccfc70c3e36d76a2
> このページなど見ますとVSの場合にはリトルエンディアンしか使えないのではないでしょうか?
> どういう場合にビッグエンディアンが有効になりますか?
>
エンディアン(複数バイトで表現される数値データのメモリ上の並び)は、CPUに依存するため
コンパイル時のターゲットCPUによって決まります。
x86/x64(.NET Framework のサポートするCPUタイプ)では、常に Little Endian になるため
BIGENDIAN が有効になることはありません。

詳しくは、「エンディアン」「Endian」あるいは、「バイトオーダー」などでいろいろ調べてみてください。
速度を気にするような実装であれば、今はよくとも後々考慮が必要ということも出てくるかもしれないので。

[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96690 ] / ▼[ 96732 ]
■96731 / 8階層)  Re[8]: バイト配列とShort配列のやり取りに関して
□投稿者/ Yammy (4回)-(2021/01/17(Sun) 17:51:29)
> BitConverter は、内部的にはバイト操作だけですが
> エラーチェックとかいくつかあるので、そういう処理がない直接操作に比べたら遅くなります。

> 整数演算にすることでかなり速度アップが期待できます。

どうもありがとうございます。
参考になりました。



> JIT分は除外していますか?
> 具体的には起動後最初の1回は、JITが動くのでその分を排除して
> 時間差を求めないと求める答えは出にくいかもしれません。

JIT分除外というのはよく言われることですが、具体的にどのようなコードで比較したら良いですか?
配列を使えば良いかと思ったのですが、
配列を使うと通常の変数よりもオーバーヘッドが大きいので
そちらの方の影響がでてしまう気がします。




[ 親 96662 / □ Tree ] 返信 編集キー/

▲[ 96731 ] / 返信無し
■96732 / 9階層)  Re[9]: バイト配列とShort配列のやり取りに関して
□投稿者/ とっちゃん (714回)-(2021/01/17(Sun) 20:49:09)
No96731 (Yammy さん) に返信


>>BitConverter は、内部的にはバイト操作だけですが
>>エラーチェックとかいくつかあるので、そういう処理がない直接操作に比べたら遅くなります。
>
>>整数演算にすることでかなり速度アップが期待できます。
>
> どうもありがとうございます。
> 参考になりました。
>

ライブラリのソースが公開されているので、読んでみるとよいと思います。
.NET Framework の場合はこちらから(別途DL可能)
https://referencesource.microsoft.com/

一応….NET Core(.NET 5〜)は別ソースなのでご注意ください(実装も結構違います)。


> JIT分除外というのはよく言われることですが、具体的にどのようなコードで比較したら良いですか?
> 配列を使えば良いかと思ったのですが、
> 配列を使うと通常の変数よりもオーバーヘッドが大きいので
> そちらの方の影響がでてしまう気がします。
>
JIT(Just In Timeの略)は、その関数が初めて呼び出された時に自分の関数のコードを実際の実行環境に
合わせて再コンパイルして実行コードを出力するという仕組みです。

.NET Framework は Java と同じように中間コードで出力されています。
それを実行時に、実際の環境(CPU)に合わせて最終的なマシン語にコンパイルされ実行されるという形になっています。
一度コンパイルすれば、そのプロセスが終了するまでオンメモリコードとしてコンパイル後コードが残ります。

仮に、ToInt16や、GetBytes みたいにごくごく小さなコードで、何度も呼び出すような場合であれば
最初の1回分も含めて、計測してもそれほどではないと思いますけどね。

[ 親 96662 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -