3

Assume I have a fixed-size bytes array and a long:

Dim abHeader(0 To 3) As Byte
Dim lHeader As Long

To transport the bytes array into the Long, with the arrays index 3 located at the 8 LSB of the Long, I currently use:

lHeader = (CLng(abHeader(0)) << 24) _
       Or (CLng(abHeader(1)) << 16) _
       Or (CLng(abHeader(2)) << 8) _
       Or (CLng(abHeader(3)))

Is there a computationally more efficient way to do this task in VB.NET? (It's being used in a time-critical loop.)

NB: Option Strict is on.

  • Don't know if this is relevant but FYI 4 bytes make an `Integer`, 8 bytes make a `Long`. – Visual Vincent Jul 11 '17 at 07:20
  • `Is there a computationally more efficient way to do this` - Is that code of yours really _**that**_ slow?? – Visual Vincent Jul 11 '17 at 07:23
  • @VisualVincent, thanks for your replies. I know how many bytes fit into standard Integers and Longs, sure ;) However, the Long will receive more data in its 32 MSB by other means. - As for efficiency: the array is dereferenced 4 times to access a byte, 4 casts to a Long need to be made, 3 LSH and 3 OR operations and a MOV are involved. Giving that the 4 bytes in question are in consecutive memory, and the target is consecutive memory as well, I should be able to move these 4 bytes Assembler-like within a single CPU cycle. Of course this is not Assembler, but the overhead seems quite large. –  Jul 11 '17 at 08:08
  • 2
    "the overhead seems quite large" - does that mean you've measured how important this is in the context of your actual application? Do you have concrete performance requirements that this is violating? If not, I suggest you don't worry about it - but develop concrete performance requirements and make sure you're measuring them. – Jon Skeet Jul 11 '17 at 08:47
  • how does the byte array filled? you may use some direct method to read an int from buffer in some scenarios – S.Serpooshan Jul 11 '17 at 09:54
  • @S.Serp, it is provided by an `IO.Stream`. In the easiest case, this is `s.Read(abHeader, 0, 4)`. However, sometimes variable constructs apply, taken from different positions and lengths, e.g.: `s.Read(abHeader, i, j)`, and also directly `abHeader(i) = v` occur. –  Jul 11 '17 at 10:04
  • @Herb : New update to my answer showing some more accurate readings. – Visual Vincent Jul 11 '17 at 11:16
  • @JonSkeet: every ns counts. I attempt to decode MP3, not for playback, rest assured. I want to do frequency-time-analysis. Basically, this is a subproblem of [this](https://stackoverflow.com/questions/44964227/how-to-access-data-obtained-by-webclient-downloaddataasync/44965076#44965076) (which is a subproblem itself, see there). In short, I really need to save time everywhere. 4 dereferences, 4 casts, 3 SHL, 3 OR, a MOV just do not seem efficient. And I do not want my app to noticably slow down other apps. –  Jul 11 '17 at 16:15
  • So you already have a performance harness for this? Are you able to share this as part of the question? (It's a lot easier to test if something helps that way...) – Jon Skeet Jul 11 '17 at 16:48

2 Answers2

2

You can try using BitConverter.ToInt32() and then CLng() on that. The BitConverter class utilizes C#'s unsafe code in order to directly cast an array of bytes into an Integer (or it does so at least as long as the startIndex parameter is divisible by 4):

Dim lHeader As Long = CLng(BitConverter.ToInt32(abHeader, 0))

UPDATE: According to my now updated online test once the JIT (Just-In-Time) compiler (thanks for reminding me, @JamesThorpe!) has done its job our methods execute pretty much equally fast.

Link: http://ideone.com/lhJ61J

Bitwise Or              : 32767
Compile time            : 00:00:00.0000725
1M iterations avg.      : 00:00:00.0000003

Convert.ToInt64()       : <CAST ERROR>
Compile time            : ???

BitConverter.ToInt32()  : 32767
Compile time            : 00:00:00.0001834
1M iterations avg.      : 00:00:00.0000003

Now if your application cannot handle a piece of code that takes 0,3 microseconds (0,0003 milliseconds) to execute, then I don't know what to tell you. ;)

Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
  • 1
    abHeader array needs to have 8 elements for BitConverter.ToInt64, otherwise an error will occur. Also its result is not as given code! – S.Serpooshan Jul 11 '17 at 09:56
  • @S.Serp : You're right about the first part, but what do you mean with _"its result is not given as code"_? – Visual Vincent Jul 11 '17 at 10:10
  • @S.Serp : Updated. Thanks for notifying me! – Visual Vincent Jul 11 '17 at 10:27
  • 1
    You need to call each method more than once to get some decent perf timings - the first call is more than likely mostly the JIT running. – James Thorpe Jul 11 '17 at 10:46
  • @JamesThorpe : Which just shows that the code is actually even faster :). Will update my test. – Visual Vincent Jul 11 '17 at 10:48
  • @JamesThorpe : Updated answer, thanks for reminding me about the JIT compilation. Ironically though I had intended to test it using loops at first, however that must've slipped my mind. ;) – Visual Vincent Jul 11 '17 at 11:14
  • @VisualVincent, thanks for the tests. Are you sure, that the compiler doesn't optimize the loop? If it's just always the same data which doesn't get modified, it might. Does the test coincidentally return the same duration when using 1m instead of 10k loops? –  Jul 11 '17 at 14:44
  • @Herb : Valid point, I'll try randomizing the values as well. Give me a couple of minutes... – Visual Vincent Jul 11 '17 at 16:52
  • @Herb : Nope, still same results. 1 million iterations with random data each time (only measuring the time it takes to convert it to a `Long`): http://ideone.com/lhJ61J – Visual Vincent Jul 11 '17 at 17:05
  • @VisualVincent the second part of my comment means the code provided by 'Herb' in original question produce different result as your code. He shifts first element in array by 24 as in `(abHeader(0)) << 24` but you use reversely `Array(3) << 24 ...`. Anyway, i think the bottleneck is not this simple bit-wise operation to cast 4 bytes to an integer as you said in your answer – S.Serpooshan Jul 12 '17 at 05:42
  • @S.Serp : I reversed it because the IDEONE system is not Windows, and therefore has a different [**endianness**](https://en.m.wikipedia.org/wiki/Endianness) (as mentioned in the comment above that line). If I wouldn't reverse the indexes then the code would've given a different result compared to `BitConverter.ToInt32()` (I'm not saying _he_ should reverse them, but I had to for it to work the same way using IDEONE). – Visual Vincent Jul 12 '17 at 07:06
0
The length of the Byte array needs to be 8 as size of long/int64 is 8

Dim abHeader(0 To 7) As Byte
Dim lHeader As Long = BitConvert.ToInt64(abHeader)

or with changed byte order:

Dim lHeader As Long = BitConvert.ToInt64(abHeader.Reverse())
David Sdot
  • 2,343
  • 1
  • 20
  • 26