Surprising results

  • Thread starter Thread starter Jack
  • Start date Start date
J

Jack

Imagine if someone asked which of the follwing two methods of encoding
an integer into a byte array was quicker.

The purpose is in preparation for sending across a socket as part of a
'header'.


If you are curious, run the code yourself.

if(args.Length != 1)
{
Console.WriteLine("Usage: {0} <iterations>",
System.AppDomain.CurrentDomain.FriendlyName);

return;
}

int[] size = new int[Convert.ToInt32(args[0])];
for(int i = 0; i < size.Length; i++)
size = i;

HiPerfTimer timer = new HiPerfTimer();

byte[] headerBuf = null;
timer.Start();
for(int i = 0; i < size.Length; i++)
{
// convert the int to a byte array in network byte
order
headerBuf = BitConverter.GetBytes(size);
}
timer.Stop();
long bitConvert = timer.usec;

byte[] bas = null;
timer.Start();
for(int i = 0; i < size.Length; i++)
{
bas = new byte[4];
bas[0] = (byte)(size >> 24);
bas[1] = (byte)(size >> 16);
bas[2] = (byte)(size >> 8);
bas[3] = (byte)(size);
}
timer.Stop();
long bitShift = timer.usec;

Console.WriteLine("BitShift method on sample of length {0}
took {1} microseconds", size.Length, bitShift );
Console.WriteLine("BitConverter method on sample of length
{0} took {1} microseconds", size.Length, bitConvert );
 
After 100 mil iterations, I get 23 seconds for the BitConverter and only 17 for
the bit shifting.

Also, for more precise results (which made little difference), I changed thebit
conversion portion into a function which is similar to the GetBytes declaration
so that it couldn't be said the the difference in time is the overhead for
function calls. If there was a difference, it is VERY VERY small.

Mythran
 
After 100 mil iterations, I get 23 seconds for the BitConverter and only 17 for
the bit shifting.


This is what my results indicated as well. I am surprised that there
could be such a difference, which makes me wonder what the
BitConverter function is doing.
 
Jack said:
Imagine if someone asked which of the follwing two methods of encoding
an integer into a byte array was quicker.

The purpose is in preparation for sending across a socket as part of a
'header'.

Two things:

1) When you post code like this, post the whole damn file. Having to write
it out is a pain, especially when you have to guess what namespaces where in
use, which leads me to
2) When you use an assembly or class that isn't part of the framework,
*ALWAYS* mention it and provide a link(or include it if appropriate). It
irks people less. The HiPerfTimer I found *doesn't* work with your code, so
for the moment I am giving up on this and moving on.
 
Touchy Touchy...I was able to write the HiPerfTimer in < 10 minutes, the missing
piece of his code was easily created automatically by the IDE when you do
File->New->Console Application :P

Is it really that aggravating? Just wondering, no offense intended...

Mythran
 
Mythran said:
Touchy Touchy...I was able to write the HiPerfTimer in < 10 minutes,
the missing piece of his code was easily created automatically by the
IDE when you do File->New->Console Application :P

Is it really that aggravating? Just wondering, no offense intended...

It is when you're trying to spend time helping people with things they
don't know how to do rather than doing things they've already done.

Wasting five minutes per question is a good way to push the number of
questions that get answered right down.

See http://www.pobox.com/~skeet/csharp/complete.html for my suggestions
on this matter. If I'm making a post and I want other people to run
some code I've written, it's up to me to make it as easy as possible
for others to run that code.
 
Mythran said:
Touchy Touchy...I was able to write the HiPerfTimer in < 10 minutes, the
missing
piece of his code was easily created automatically by the IDE when you do
File->New->Console Application :P

Is it really that aggravating? Just wondering, no offense intended...

It can be. Here is a rough breakdown of what I did last night:
1) Ok, no using statements, but everything looks like just System, except
for HiPerfTimer.
2) type out using System;, public class Test { public static void
Main(string[] args) { <paste> } }
3) HiPerfTimer doesn't look familiar, lets look it up to figure out what
namespace its in
4) Hrmm, not in the 1.1 or 2.0 frameworks, so it must be third party
5) google
6) Code Project -> HiPerfTimer
7) download code, open file, copy, paste into other file
8) compile, error out.
9) Come complain becuase I've had to take too many steps to try to get this
to work.


Also, for benchmarks like this I don't use the IDE, ;). I use a quick little
editor called Crimson Editor for benchmarks and quick tests, instead of
loading VS for every file I try to compile(also, the editor helps since I
only have one version of visual studio installed at home, I can compile with
seperate compilers to see if runtime version changes the results).

Also, I would worry about using a benchmark where my timer and your timer
were written by different people, its possible the error was in your timer
code, either due to a bug in the runtime or due to a mistake you made. Or I
could make a mistake and skew my results.

Either way, its always best to provide everything. Like Jon said, 5 minutes
for every message doesn't let you get through too many.
 
Anywho, hope y'all didn't get offended by my previous reply to ya...just
wonderin'..

:) Now I know...and know is half...<zZzZzZ>

Mythran
 
Two things:

1) When you post code like this, post the whole damn file. Having to write
it out is a pain, especially when you have to guess what namespaces where in
use, which leads me to
2) When you use an assembly or class that isn't part of the framework,
*ALWAYS* mention it and provide a link(or include it if appropriate). It
irks people less. The HiPerfTimer I found *doesn't* work with your code, so
for the moment I am giving up on this and moving on.

In response to your 'Two Things'

1.) "...post the whole damn file" - really ?!?!? It just happens that
the 'whole damn file' is littered with snippets of code that would
probably confuse you, and would never link since they reference many
non .NET assemblies. OK I admit I could have yanked the code off into
its own project and given the entire listing like that, but, surely
you could just as easily have done that if you were remotely
interested in the issue. The code as presented illustrates the issue
without the burden of non-relevant code. Get off your high horse.

2) "When you use an assembly or class ..." Wow, HiPerfTimer gives you
an unresolved symbol ? Comment it out ! I agree you might be
slightly inconvenienced, but if you are interested in the issue, it's
pretty insignificant. Its use is obvious, you probably already have
your own High Performance Timer class you could easily slot in - if
not here it is


using System.Runtime.InteropServices;
class HiPerfTimer
{
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceCounter(out long
lpPerformanceCount);

[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(out long
lpFrequency);

private long start, stop, freq;

// Constructor
public HiPerfTimer()
{
start= 0;
stop= 0;

if (QueryPerformanceFrequency(out freq) == false)
{
// high-performance counter not supported
throw new System.Exception("Performance Counter not
supported - bizarre !");
}
}

// Start the timer
public void Start()
{
QueryPerformanceCounter(out start);
}

// Stop the timer
public void Stop()
{
QueryPerformanceCounter(out stop);
}

// Returns the duration of the timer (in microseconds)
public long usec
{
get
{
return (long)(((double)(stop - start) / (double) freq
)*(double)1000000);
}
}
// Returns the duration of the timer (in milliseconds)
public long msec
{
get
{
return (long)(((double)(stop - start) / (double) freq
)*(double)1000);
}
}
// Returns the duration of the timer (in seconds)
public double sec
{
get
{
return (long)((double)(stop - start) / (double) freq );
}
}

}

Again, the relevant code is present, in fact I have provided more code
than I really should have, I could easily have simply asked this
question

why is

BitConverter.GetBytes()

so slow compared to

byte[] myConvert(int val)
{
byte[] ret = new byte[4];
ret[0] = (byte)(val >> 24);
ret[1] = (byte)(val >> 16);
ret[2] = (byte)(val >> 8);
ret[3] = (byte)(val);
return ret;
}

But I decided to give you a framework for illustrating how slow it is.

As a side note: I realise you probably spent a long time becoming an
MVP, however, don't let it go to your head. There are plenty of us
out there who are also MVPs who are more humble than you, and don't
choose to advertise the fact by adding it to our signatures.
 
Jack said:
In response to your 'Two Things'

1.) "...post the whole damn file" - really ?!?!? It just happens that
the 'whole damn file' is littered with snippets of code that would
probably confuse you, and would never link since they reference many
non .NET assemblies. OK I admit I could have yanked the code off into
its own project and given the entire listing like that, but, surely
you could just as easily have done that if you were remotely
interested in the issue. The code as presented illustrates the issue
without the burden of non-relevant code. Get off your high horse.

It illustrates the issue *with* the burden of not including all
relevant code though.
2) "When you use an assembly or class ..." Wow, HiPerfTimer gives you
an unresolved symbol ? Comment it out ! I agree you might be
slightly inconvenienced, but if you are interested in the issue, it's
pretty insignificant. Its use is obvious, you probably already have
your own High Performance Timer class you could easily slot in - if
not here it is

It would have been better to have either included it originally, or
(preferably, IMO) use a test which doesn't require a high performance
timer. The difference between 23 seconds and 17 seconds doesn't require
a high performance timer for measurement.
Again, the relevant code is present, in fact I have provided more code
than I really should have, I could easily have simply asked this
question

why is

BitConverter.GetBytes()

so slow compared to

byte[] myConvert(int val)
{
byte[] ret = new byte[4];
ret[0] = (byte)(val >> 24);
ret[1] = (byte)(val >> 16);
ret[2] = (byte)(val >> 8);
ret[3] = (byte)(val);
return ret;
}

Better, you could have presented a short but *complete* program to
demonstrate the problem. Here's such a program, which is only 5 lines
shorter, but would have saved anyone interested in it some time.
Personally I just skipped over your post as it was obvious it would
require more work to look at your question than it would to look at
someone else's.

using System;

public class Test
{
static void Main(string[] args)
{
if (args.Length==0)
{
Console.WriteLine ("Usage: Test <number of iterations>");
return;
}

int iterations = int.Parse(args[0]);

{
DateTime start = DateTime.Now;
for (int i=0; i < iterations; i++)
{
byte[] buf = BitConverter.GetBytes(i);
}
DateTime end = DateTime.Now;
Console.WriteLine ("Time taken by BitConverter: {0}",
end-start);
}

{
DateTime start = DateTime.Now;
for (int i=0; i < iterations; i++)
{
byte[] buf = SimpleConvert(i);
}
DateTime end = DateTime.Now;
Console.WriteLine ("Time taken by SimpleConvert: {0}",
end-start);
}
}

static byte[] SimpleConvert (int i)
{
byte[] ret = new byte[4];
ret[0] = (byte) (i >> 24);
ret[1] = (byte) (i >> 16);
ret[2] = (byte) (i >> 8);
ret[3] = (byte) i;
return ret;
}
}

You could have also given the results you saw on your system. For
example:

Sample run:
C:\test>test 500000000
Time taken by BitConverter: 00:00:26.8281250
Time taken by SimpleConvert: 00:00:10.7500000
But I decided to give you a framework for illustrating how slow it is.

No, you gave *part* of a framework.

See http://www.yoda.arachsys.com/csharp/complete.html for more
discussion of why it's worth posting short but complete programs.
As a side note: I realise you probably spent a long time becoming an
MVP, however, don't let it go to your head. There are plenty of us
out there who are also MVPs who are more humble than you, and don't
choose to advertise the fact by adding it to our signatures.

IMO, that defeats part of the point of having MVPs - one of the things
about MVPs is that they should be credible. In other words, if someone
comes to a newsgroup and sees a response from someone with an MVP tag,
it gives them confidence that that person generally knows what they're
talking about within their MVP area of expertise. It's far from
uncommon for MVPs to do this. (In fact, almost all the MVPs I see in
the .NET newsgroups do it.)

Are you an MVP out of interest? You sort of implied that you are in
that last sentence, without out-right stating it...
 
Jon Skeet said:
You could have also given the results you saw on your system. For
example:

Sample run:
C:\test>test 500000000
Time taken by BitConverter: 00:00:26.8281250
Time taken by SimpleConvert: 00:00:10.7500000

Jon,

Just ran your code to illustrate the great job MS has done in v2.0

F:\vs2005b1\bugs>bitconv 500000000
Time taken by BitConverter: 00:00:10.1699122
Time taken by SimpleConvert: 00:00:13.9547660

Willy.
 
Jack said:
In response to your 'Two Things'

1.) "...post the whole damn file" - really ?!?!? It just happens that
the 'whole damn file' is littered with snippets of code that would
probably confuse you, and would never link since they reference many
non .NET assemblies. OK I admit I could have yanked the code off into
its own project and given the entire listing like that, but, surely
you could just as easily have done that if you were remotely
interested in the issue. The code as presented illustrates the issue
without the burden of non-relevant code. Get off your high horse.

Well, I had assumed you had written a simple, correct test. If you can't be
bothered to write a simple test, I certainly can't be bothered to examine
your results.
2) "When you use an assembly or class ..." Wow, HiPerfTimer gives you
an unresolved symbol ? Comment it out ! I agree you might be
slightly inconvenienced, but if you are interested in the issue, it's
pretty insignificant. Its use is obvious, you probably already have
your own High Performance Timer class you could easily slot in - if
not here it is

What point is there if the timing difference is actually a result of *your*
timer class instead of the two classes? I certainly had no reason to assume
that your code was right. Beyond that, why should I have to fix your code so
it works, you should have posted code that works right to begin with.
Again, the relevant code is present, in fact I have provided more code
than I really should have, I could easily have simply asked this
question

why is

BitConverter.GetBytes()

so slow compared to

byte[] myConvert(int val)
{
byte[] ret = new byte[4];
ret[0] = (byte)(val >> 24);
ret[1] = (byte)(val >> 16);
ret[2] = (byte)(val >> 8);
ret[3] = (byte)(val);
return ret;
}

But I decided to give you a framework for illustrating how slow it is.

That would probably have gotten you ignored also, although it may have been
interesting enough to examine. Your actual post was pretty much an automatic
skip due to the errors in the code.

Sorry to say, your posting was bad and was the reason why you had only one
response. It simply was not worth it.
As a side note: I realise you probably spent a long time becoming an
MVP, however, don't let it go to your head. There are plenty of us
out there who are also MVPs who are more humble than you, and don't
choose to advertise the fact by adding it to our signatures.

*shrug*, I have alot to do. If you are an MVP, as you imply, then I suspect
either you don't have anything else going on or you are far to dedicated.
Woudl *you* really have bothered to answer your question? Or would you have
moved on to the next one where someone actually respected the expectations
of the group?
 
Back
Top