Clash of the unlikely: C# versus.. RealBASIC?
tomgrimshaw.wordpress.com
tomgrimshaw.wordpress.com
In reality, you'd not create the encoding each time, and either use a StringBuilder to build up the string, or just use Encoding.GetString.
C# on my machine:
As per blog: 50s
Creating Encoding once: 24s
Using StringBuilder: 11s
Using Encoding.GetString: 2.5s
Use Random.NextBytes: 2s
RealBASIC on my machine:
As per blog: 27s
Interestingly despite advice I'd found on the internet[1] using array joining didn't really make the RealBASIC version any faster, so from what I can tell it's generally as fast as it could be using the out of the box libraries.
(My machine isn't exactly idle and ideal for microbenchmarking atm, but the differences are large enough for that not to matter)
This doesn't really provide a good evaluation on the state of JITs.
[1] http://forums.realsoftware.com/viewtopic.php?f=13&t=2510...
[Edit: Added RealBASIC times]
By the way, this leads me to one question: does the code
string a = "a" + "b" + "c" + "d";
generate one or three memory allocations (one to compute each concatenation)?Strings are outwardly immutable, so concatenating any two strings will create an entirely new String object, along with the memory allocation needed for that. However, there are some internal methods that allow String and other classes in the same corelib assembly (such as StringBuilder) to mutate them.
`string a = x + y + z` will be translated into `string a = String.Concat(x, y, z)`. Because it knows the final length it can use the internal String methods to allocate a new String long enough to fit all the arguments and then copy the characters into that without any intermediate representations. [1]
In a loop it doesn't know how big the final result will be, and so has to create a new String object on each iteration. If you're doing 16000000 concatenations as in this test that's a lot of intermediate strings that are largely being wasted.
StringBuilders have a private String object and use the internal String methods to mutate that when you call Append. When you call ToString it returns a copy of the private String object. Again, this avoids creating lots of new intermediate String objects, but does have the overhead of having to guess how large the initial String should be, and growing it if you append too much (similar to how Lists work).
[1] I believe in Java it's translated into using StringBuffer or StringBuilder, which has a small overhead of creating the StringBuffer/StringBuilder object.
[Edit: Corrected how String.Concat and StringBuilders work based on the Mono codebase]
Note that the actual magic consists of internal methods in String.cs:
- FormatHelper https://github.com/mono/mono/blob/master/mcs/class/corlib/Sy...
- InternalSetChar https://github.com/mono/mono/blob/master/mcs/class/corlib/Sy...
- CharCopy https://github.com/mono/mono/blob/master/mcs/class/corlib/Sy...
The RealBASIC version calls the inbuilt "chr" function while in C# it calls:
public static char Chr(byte src)
{
return (System.Text.Encoding.GetEncoding("iso-8859-1").GetChars(new byte[] { src })[0]);
}
I'm sure there's some overhead to calling this 16 million times.The recommended way is to use System.Diagnostics.Stopwatch, since that uses the CPU's built-in high-performance timer (HPET).
With StringBuilder: 2.61 seconds With String Concat: 3.03 seconds
I have to assume there's another loop running these, but I'm not seeing it. Each dictionary has 1 million elements at the end.
I just implemented it inside a junit test, here's the class:
Turns out java performance isn't bad at all.
EDIT:
I'm a dumbass, was converting nanos to full seconds. Post has been edited.