struct mydata
{
public int id;
public string data;
}
class Program
{
static void Main(string[] args)
{
List<mydata> myc = new List<mydata>();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
mydata d = new mydata();
d.id = i;
d.data = string.Format("DataValue {0}",i);
myc.Add(d);
}
stopwatch.Stop();
Console.WriteLine("End: {0}", stopwatch.ElapsedMilliseconds);
}
Whys is this code above so SLOW..?
On an older laptop the times are:
C# code above: 1500ms
Similar code in Delphi: 450ms....
I then changed the code to a KeyValue/Pair (see below):
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var list = new List<KeyValuePair<int , string>>();
for (int i = 0; i < 1000000; i++)
{
list.Add(new KeyValuePair<int,string>(i, "DataValue" + i));
}
stopwatch.Stop();
Console.WriteLine("End: {0}", stopwatch.ElapsedMilliseconds);
Console.ReadLine();
This improved the time to 1150ms..
If I remove the '+ i' the time is < 300ms
If I try and replace it with a StringBuilder, the timing is similar.
StringBuilder sb = new StringBuilder();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var list = new List<KeyValuePair<int, string>>();
for (int i = 0; i < 1000000; i++)
{
sb.Append("DataValue");
sb.Append(i);
list.Add(new KeyValuePair<int, string>(i, sb.ToString()));
sb.Clear();
}
stopWatch.Stop();
Console.WriteLine("End: {0}", stopWatch.ElapsedMilliseconds);
Console.ReadLine();
Is slightly better.. If you remove the sb.Append(i) its very fast..
It would appear that any time you have to add an Int to a string/stringbuilder its VERY SLOW..
Can I speed this up in any way ??
EDIT **
The code below is the quickest I can get after making suggestions:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication1
{
struct mydata
{
public int id;
public string data;
}
class Program
{
static void Main(string[] args)
{
List<mydata> myc = new List<mydata>();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
mydata d = new mydata();
d.id = i;
d.data = "DataValue " + i.ToString();
myc.Add(d);
}
stopwatch.Stop();
Console.WriteLine("End: {0}", stopwatch.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
If I replace the line:
d.data = "DataValue " + i.ToString();
with:
d.data = "DataValue ";
On my home machine this goes from 660ms -> 31ms..
Yes.. its 630ms slower with the '+ i.ToString()'
But still 2x faster than boxing/string.format etc etc..
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var list = new List<KeyValuePair<int, string>>();
for (int i = 0; i < 1000000; i++)
{
list.Add(new KeyValuePair<int, string>(i, "DataValue" +i.ToString()));
}
stopwatch.Stop();
Console.WriteLine("End: {0}", stopwatch.ElapsedMilliseconds);
Console.ReadLine();
is 612ms.. (no difference in speed if List>(1000000); is pre-initialised).
The problem with your first two examples is that the integer must first be boxed and then converted to a string. The boxing causes the code to be slower.
For example, in this line:
d.data = string.Format("DataValue {0}", i);
the second parameter to string.Format is object, which causes boxing of i. See the intermediate language code for confirmation of this:
...
box int32
call string [mscorlib]System.String::Format(string, object)
...
Similarly this code:
d.data = "DataValue " + i;
is equivalent to this:
d.data = String.Concat("DataValue ", i);
This uses the overload of String.Concat with parameters of type object so again this involves a boxing operation. This can be seen in the generated intermediate language code:
...
box int32
call string [mscorlib]System.String::Concat(object, object)
...
For better performance this approach avoids the boxing:
d.data = "DataValue " + i.ToString();
Now the intermediate language code doesn't include the box instruction and it uses the overload of String.Concat that takes two strings:
...
call instance string [mscorlib]System.Int32::ToString()
call string [mscorlib]System.String::Concat(string, string)
...
On my machine:
... String.Format("DataValue {0}", i ) // ~1650ms
... String.Format("DataValue {0}", "") // ~1250ms
... new MyData {Id = i, Data = "DataValue {0}" + i} // ~1200ms
As Mark said, there's a boxing operation involved.
For this specific case, when you get your DataValue based on your id, you could to create a get property or to override ToString() method to do that operation just when you need it.
public override string ToString()
{
return "DataValue {0}" + Id;
}
There are a lot of things wrong with the above which will be affecting your results.
First, none of the comparisons you've done are equal. In both you have a list, and use Add, what you add to the list won't affect the time, changing the declaration of the List to var won't affect the time.
I'm not convinced by the boxing argument put up by Mark, this can be a problem, but I'm pretty certain in the first case there is an implicit call to .ToString. This has its own overhead, and would be needed even if the int is boxed.
Format is quite an expensive operation.
The second version has a string concatenation which is probably cheaper than a .Format.
The third is just expensive all the way. Using a string builder like that is not efficient. Internally a stringbuilder is just a list. When you do a .ToString on it you essentially do a big concat operation then.
The reason some of the operations might suddenly run really quickly if you take out a critical line is that the compile can optimise out bits of code. If it seems to be doing the same thing over and over it might not do it (Gross over simplification).
Right, so here's my suggestion:
The first version is probably the nearest to being "right" in my mind. What you could do is defer some of the processing. Take the object mydata and set a string property AND an int property. Then only when you need to do the read of the string produce the output via a concat. Save that if you're going to repeat the print operation a lot. It won't necessarilly be quicker in the way you expect.
Another major performance killer in this code is the List. Internally it stores the items in an array. When you call Add, it checks if the new Item can fit into the array (EnsureCapacitiy). When it needs more room, it will create a NEW array with double the size, and then COPY the items from the old array into the new one. You can see all this going on if you check out List.Add in Reflector.
So in your code with 1,000,000 items, it needs to copy the array some 25 times, and they're bigger each time.
If your change your code to
var list = new List<KeyValuePair<int, string>>(1000000);
you should see a dramatic increase in speed. Let us know how you fare!
Regards,
GJ
Related
I am trying to seek the way making more performant deserialization process in our application. I created simple test below. When I check the results, it seems like JsonConvert.DeserializeObject working much faster after first iteration.
[TestMethod]
public void DeserializeObjectTest()
{
int count = 5;
for (int i = 0; i < count; i++)
{
CookieCache cookieCache = new CookieCache()
{
added = DateTime.UtcNow.AddDays(-1),
VisitorId = Guid.NewGuid().ToString(),
campaigns = new List<string>() { "qqq", "www", "eee" },
target_dt = "3212018",
updated = DateTime.UtcNow
};
Stopwatch stopwatch = Stopwatch.StartNew();
string serializeObject = JsonConvert.SerializeObject(cookieCache);
CookieCache deserializeObject = JsonConvert.DeserializeObject<CookieCache>(serializeObject);
stopwatch.Stop();
double stopwatchElapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds;
Debug.WriteLine("iteration " + i + ": " + stopwatchElapsedMilliseconds);
}
And my results:
I think I am using Stopwatch correctly. So does JSON.NET is using some sort of internal caching or optimization process on deserialization on following calls?
Since this is a web application, of course, I am getting similar results in my logs (280.6466) for every single web request.
So am I missing something on my test? Or is it expected behavior?
I was optimizing my code, and I noticed that using properties (even auto properties) has a profound impact on the execution time. See the example below:
[Test]
public void GetterVsField()
{
PropertyTest propertyTest = new PropertyTest();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
propertyTest.LoopUsingCopy();
Console.WriteLine("Using copy: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Restart();
propertyTest.LoopUsingGetter();
Console.WriteLine("Using getter: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Restart();
propertyTest.LoopUsingField();
Console.WriteLine("Using field: " + stopwatch.ElapsedMilliseconds / 1000.0);
}
public class PropertyTest
{
public PropertyTest()
{
NumRepet = 100000000;
_numRepet = NumRepet;
}
int NumRepet { get; set; }
private int _numRepet;
public int LoopUsingGetter()
{
int dummy = 314;
for (int i = 0; i < NumRepet; i++)
{
dummy++;
}
return dummy;
}
public int LoopUsingCopy()
{
int numRepetCopy = NumRepet;
int dummy = 314;
for (int i = 0; i < numRepetCopy; i++)
{
dummy++;
}
return dummy;
}
public int LoopUsingField()
{
int dummy = 314;
for (int i = 0; i < _numRepet; i++)
{
dummy++;
}
return dummy;
}
}
In Release mode on my machine I get:
Using copy: 0.029
Using getter: 0.054
Using field: 0.026
which in my case is a disaster - the most critical loop just can't use any properties if I want to get maximum performance.
What am I doing wrong here? I was thinking that these would be inlined by the JIT optimizer.
Getters/Setters are syntactic sugar for methods with a few special conventions ("value" variable in a setter", and no visible parameter list).
According to this article, "If any of the method's formal arguments are structs, the method will not be inlined." -- ints are structs. Therefore, I think this limitation applies.
I haven't looked at the IL produced by the following code, but I did get some interesting results that I think shows this working this way...
using System;
using System.Diagnostics;
public static class Program{
public static void Main()
{
PropertyTest propertyTest = new PropertyTest();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
propertyTest.LoopUsingField();
Console.WriteLine("Using field: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Restart();
propertyTest.LoopUsingBoxedGetter();
Console.WriteLine("Using boxed getter: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Restart();
propertyTest.LoopUsingUnboxedGetter();
Console.WriteLine("Using unboxed getter: " + stopwatch.ElapsedMilliseconds / 1000.0);
}
}
public class PropertyTest
{
public PropertyTest()
{
_numRepeat = 1000000000L;
_field = 1;
Property = 1;
IntProperty = 1;
}
private long _numRepeat;
private object _field = null;
private object Property {get;set;}
private int IntProperty {get;set;}
public void LoopUsingBoxedGetter()
{
for (long i = 0; i < _numRepeat; i++)
{
var f = Property;
}
}
public void LoopUsingUnboxedGetter()
{
for (long i = 0; i < _numRepeat; i++)
{
var f = IntProperty;
}
}
public void LoopUsingField()
{
for (long i = 0; i < _numRepeat; i++)
{
var f = _field;
}
}
}
This produces.. ON MY MACHINE, OS X (recent version of Mono), these results (in seconds):
Using field: 2.606
Using boxed getter: 2.585
Using unboxed getter: 2.71
You say you are optimizing your code, but I am curious as to how, what the functionality is supposed to be, and what the source data coming into this is as well as it's size as this is clearly not "real" code. If you are parsing a large list of data in consider utilizing the BinarySearch functionality. This is significantly faster than, say the .Contains() function with very large sets of data.
List<int> myList = GetOrderedList();
if (myList.BinarySearch(someValue) < 0)
// List does not contain data
Perhaps you are simply looping through data. If you are looping through data and returning a value perhaps you may want to utilize the yield keyword. Additionally consider the potential use of the parallel library if you can, or utilize your own thread management.
This does not seem like what you want judging by the posted source but it was very generic so I figured this was worth mentioning.
public IEnumerable<int> LoopUsingGetter()
{
int dummy = 314;
for (int i = 0; i < NumRepet; i++)
{
dummy++;
yield return dummy;
}
}
[ThreadStatic]
private static int dummy = 314;
public static int Dummy
{
get
{
if (dummy != 314) // or whatever your condition
{
return dummy;
}
Parallel.ForEach (LoopUsingGetter(), (i)
{
//DoWork(), not ideal for given example, but due to the generic context this may help
dummy += i;
});
}
return dummy;
}
Follow the 80/20 performance rule instead of micro-optimizing.
Write code for maintainability, instead of performance.
Perhaps Assembly language is the fastest but that does not mean we should use Assembly language for all purposes.
You are running the loop 100 million times and the difference is 0.02 millisecond or 20 microseconds. Calling a function will have some overhead but in most cases it does not matter. You can trust the compiler to inline or do advanced things.
Directly accessing the field will be problematic in 99% of the cases as you will not have control of where all your variables are referenced and fixing at too many places when you find something is wrong.
You should stop the stop watch when it completes the loop, your stopwatch is still running when you are writing to console this can add additional time that can skew your results.
[Test]
public void GetterVsField()
{
PropertyTest propertyTest = new PropertyTest();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
propertyTest.LoopUsingCopy();
stopwatch.Stop();
Console.WriteLine("Using copy: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Reset();
stopwatch.Start();
propertyTest.LoopUsingGetter();
stopwatch.Stop();
Console.WriteLine("Using getter: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Reset();
stopwatch.Start();
propertyTest.LoopUsingField();
stopwatch.Stop();
Console.WriteLine("Using field: " + stopwatch.ElapsedMilliseconds / 1000.0);
}
You have to check if optimize code checkbox is checked.
If it is not checked, access to the property is still method call
If it is checked the property is in-lined and the performance is the same as with direct field access because the JITed code will be the same
There is more restriction about inlinig in X64 JIT compiler. More information about JIT64 inlining optimization is there:
David Broman's CLR Profiling API Blog: Tail call JIT conditions.
please see point #3 The caller or callee return a value type.
If your property will return reference type, the property getter will be in-lined.
It means that the property int NumRepet { get; set; } is not inlined but object NumRepet { get; set; } will be inlined if you don't break another restriction.
The optimization of X64 JIT is poor and this is why new one will be introduced as John mention
Why is StringBuilder slower when compared to + concatenation?
StringBuilder was meant to avoid extra object creation, but why does it penalize performance?
static void Main(string[] args)
{
int max = 1000000;
for (int times = 0; times < 5; times++)
{
Console.WriteLine("\ntime: {0}", (times+1).ToString());
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
}
sw.Stop();
Console.WriteLine("String +\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
sw = Stopwatch.StartNew();
for (int j = 0; j < max; j++)
{
StringBuilder msg = new StringBuilder();
msg.Append("Your total is ");
msg.Append("$500 ");
msg.Append(DateTime.Now);
}
sw.Stop();
Console.WriteLine("StringBuilder\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
Console.Read();
}
EDIT: Moving out of scope variables as suggested:
Change so that the StringBuilder isn't instantiated all the time, instead .Clear() it:
time: 1
String + : 3348ms
StringBuilder : 3151ms
time: 2
String + : 3346ms
StringBuilder : 3050ms
etc.
Note that this still tests exactly the same functionality, but tries to reuse resources a bit smarter.
Code: (also live on http://ideone.com/YuaqY)
using System;
using System.Text;
using System.Diagnostics;
public class Program
{
static void Main(string[] args)
{
int max = 1000000;
for (int times = 0; times < 5; times++)
{
{
Console.WriteLine("\ntime: {0}", (times+1).ToString());
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
}
sw.Stop();
Console.WriteLine("String +\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
{
Stopwatch sw = Stopwatch.StartNew();
StringBuilder msg = new StringBuilder();
for (int j = 0; j < max; j++)
{
msg.Clear();
msg.Append("Your total is ");
msg.Append("$500 ");
msg.Append(DateTime.Now);
}
sw.Stop();
Console.WriteLine("StringBuilder\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
}
}
Console.Read();
}
}
You are creating a new instance of StringBuilder with every iteration, and that incurs some overhead. Since you are not using it for what it's actually meant to do (ie: build large strings which would otherwise require many string concatenation operations), it's not surprising to see worse performance than concatenation.
A more common comparison / usage of StringBuilder is something like:
string msg = "";
for (int i = 0; i < max; i++)
{
msg += "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
}
StringBuilder msg_sb = new StringBuilder();
for (int j = 0; j < max; j++)
{
msg_sb.Append("Your total is ");
msg_sb.Append("$500 ");
msg_sb.Append(DateTime.Now);
}
With this, you'll observe a significant performance difference between StringBuilder and concatenation. And by "significant" I mean orders of magnitude, not the ~ 10% difference you are observing in your examples.
Since StringBuilder doesn't have to build tons of intermediary strings that will just get thrown away, you get much better performance. That's what it's meant for. For smaller strings, you are better off using string concatenation for simplicity and clarity.
The benefits of StringBuilder should be noticeable with longer strings.
Every time you concatenate a string you create a new string object, so the longer the string, the more is needed to copy from the old string to the new string.
Also, creating many temporary objects may have an adverse effect on performance that is not measurable by a StopWatch, because it "pollutes" the managed heap with temporary objects and may cause more garbage collection cycles.
Modify your test to create (much) longer strings and use (many) more concatenations / append operations and the StringBuilder should perform better.
Note that
string msg = "Your total is ";
msg += "$500 ";
msg += DateTime.Now;
compiles down to
string msg = String.Concat("Your total is ", "$500 ");
msg = String.Concat(msg, DateTime.Now.ToString());
This totals two concats and one ToString per iteration. Also, a single String.Concat is really fast, because it knows how large the resulting string will be, so it only allocates the resulting string once, and then quickly copies the source strings into it. This means that in practice
String.Concat(x, y);
will always outperform
StringBuilder builder = new StringBuilder();
builder.Append(x);
builder.Append(y);
because StringBuilder cannot take such shortcuts (you could call a thirs Append, or a Remove, that's not possible with String.Concat).
The way a StringBuilder works is by allocating an initial buffer and set the string length to 0. With each Append, it has to check the buffer, possibly allocate more buffer space (usually copying the old buffer to the new buffer), copy the string and increment the string length of the builder. String.Concat does not need to do all this extra work.
So for simple string concatenations, x + y (i.e., String.Concat) will always outperform StringBuilder.
Now, you'll start to get benefits from StringBuilder once you start concatenating lots of strings into a single buffer, or you're doing lots of manipulations on the buffer, where you'd need to keep creating new strings when not using a StringBuilder. This is because StringBuilder only occasionally allocates new memory, in chunks, but String.Concat, String.SubString, etc. (nearly) always allocate new memory. (Something like "".SubString(0,0) or String.Concat("", "") won't allocate memory, but those are degenerate cases.)
In addition to not using StringBuilder as in the most efficient manner, you're also not using string concatenation as efficiently as possible. If you know how many strings you're concatenating ahead of time, then doing it all on one line should be fastest. The compiler optimizes the operation so that no intermediate strings are generated.
I added a couple more test cases. One is basically the same as what sehe suggested, and the other generates the string in one line:
sw = Stopwatch.StartNew();
builder = new StringBuilder();
for (int j = 0; j < max; j++)
{
builder.Clear();
builder.Append("Your total is ");
builder.Append("$500 ");
builder.Append(DateTime.Now);
}
sw.Stop();
Console.WriteLine("StringBuilder (clearing)\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
msg = "Your total is " + "$500" + DateTime.Now;
}
sw.Stop();
Console.WriteLine("String + (one line)\t: {0}ms", ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6));
And here is an example of the output I see on my machine:
time: 1
String + : 3707ms
StringBuilder : 3910ms
StringBuilder (clearing) : 3683ms
String + (one line) : 3645ms
time: 2
String + : 3703ms
StringBuilder : 3926ms
StringBuilder (clearing) : 3666ms
String + (one line) : 3625ms
In general:
- StringBuilder does better if you're building a large string in a lot of steps, or you don't know how many strings will be concatenated together.
- Mashing them all together in a single expression is better whenever it's a reasonable option option.
I think its better to compare effeciancy between String and StringBuilder rather then time.
what msdn says:
A String is called immutable because its value cannot be modified once it has been created. Methods that appear to modify a String actually return a new String containing the modification. If it is necessary to modify the actual contents of a string-like object, use the System.Text.StringBuilder class.
string msg = "Your total is "; // a new string object
msg += "$500 "; // a new string object
msg += DateTime.Now; // a new string object
see which one is better.
Here is an example that demonstrates a situation in which StringBuilder will execute more quickly than string concatenation:
static void Main(string[] args)
{
const int sLen = 30, Loops = 10000;
DateTime sTime, eTime;
int i;
string sSource = new String('X', sLen);
string sDest = "";
//
// Time StringBuilder.
//
for (int times = 0; times < 5; times++)
{
sTime = DateTime.Now;
System.Text.StringBuilder sb = new System.Text.StringBuilder((int)(sLen * Loops * 1.1));
Console.WriteLine("Result # " + (times + 1).ToString());
for (i = 0; i < Loops; i++)
{
sb.Append(sSource);
}
sDest = sb.ToString();
eTime = DateTime.Now;
Console.WriteLine("String Builder took :" + (eTime - sTime).TotalSeconds + " seconds.");
//
// Time string concatenation.
//
sTime = DateTime.Now;
for (i = 0; i < Loops; i++)
{
sDest += sSource;
//Console.WriteLine(i);
}
eTime = DateTime.Now;
Console.WriteLine("Concatenation took : " + (eTime - sTime).TotalSeconds + " seconds.");
Console.WriteLine("\n");
}
//
// Make the console window stay open
// so that you can see the results when running from the IDE.
//
}
Result # 1
String Builder took :0 seconds.
Concatenation took : 8.7659616 seconds.
Result # 2
String Builder took :0 seconds.
Concatenation took : 8.7659616 seconds.
Result # 3
String Builder took :0 seconds.
Concatenation took : 8.9378432 seconds.
Result # 4
String Builder took :0 seconds.
Concatenation took : 8.7972128 seconds.
Result # 5
String Builder took :0 seconds.
Concatenation took : 8.8753408 seconds.
StringBulder is much faster than + concatenation..
What is the difference between these two ways of converting a string to System.Guid? Is there a reason to choose one over the other?
var myguid = Guid.Parse("9546482E-887A-4CAB-A403-AD9C326FFDA5");
or
var myguid = new Guid("9546482E-887A-4CAB-A403-AD9C326FFDA5");
A quick look in the Reflector reveals that both are pretty much equivalent.
public Guid(string g)
{
if (g == null)
{
throw new ArgumentNullException("g");
}
this = Empty;
GuidResult result = new GuidResult();
result.Init(GuidParseThrowStyle.All);
if (!TryParseGuid(g, GuidStyles.Any, ref result))
{
throw result.GetGuidParseException();
}
this = result.parsedGuid;
}
public static Guid Parse(string input)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
GuidResult result = new GuidResult();
result.Init(GuidParseThrowStyle.AllButOverflow);
if (!TryParseGuid(input, GuidStyles.Any, ref result))
{
throw result.GetGuidParseException();
}
return result.parsedGuid;
}
Use the version that is the most readable to you. The two are implemented almost exactly the same way.
The only real difference is that the constructor initializes itself to Guid.Empty before attempting the parse. However, the effective code is identical.
That being said, if the Guid is coming from user input, then Guid.TryParse would be better than either option. If this Guid is hard coded, and always valid, either of the above are perfectly reasonable options.
I tried performance on one milion guids and Guid.Parse seems to be a insignificantly faster. It made 10-20 milisecods difference of 800 miliseconds of total creation on my PC.
public class Program
{
public static void Main()
{
const int iterations = 1000 * 1000;
const string input = "63559BC0-1FEF-4158-968E-AE4B94974F8E";
var sw = Stopwatch.StartNew();
for (var i = 0; i < iterations; i++)
{
new Guid(input);
}
sw.Stop();
Console.WriteLine("new Guid(): {0} ms", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (var i = 0; i < iterations; i++)
{
Guid.Parse(input);
}
sw.Stop();
Console.WriteLine("Guid.Parse(): {0} ms", sw.ElapsedMilliseconds);
}
}
And output:
new Guid(): 804 ms
Guid.Parse(): 791 ms
In .Net framework v1.1 exists only 1 way -> var myguid = new Guid("9546482E-887A-4CAB-A403-AD9C326FFDA5");
Guid.Parse became available later.
I'm testing the efficiency of an extension method to see which permutation would be the fastest in terms of processing time. Memory consumption isn't an issue at this point..
I've created a small console app to generate an array of of random strings, which then has the extension methods applied to it. I'm currently using the StopWatch class to measure the time taken to run the extension methods. I then average to total time of each method over a number of iterations.
I'm not excluding highest or lowest results at this point.
Extension Methods being tested:
public static String ToString1(this String[] s) {
StringBuilder sb = new StringBuilder();
foreach (String item in s) {
sb.AppendLine(item);
}
return sb.ToString();
}
public static String ToString2(this String[] s) {
return String.Join("\n", s);
}
Program.cs
static void Main(string[] args)
{
long s1Total = 0;
long s2Total = 0;
double s1Avg = 0;
double s2Avg = 0;
int iteration = 1;
int size = 100000;
while (iteration <= 25)
{
Console.WriteLine("Iteration: {0}", iteration);
Test(ref s1Total, ref s2Total, ref iteration, size);
}
s1Avg = s1Total / iteration;
s2Avg = s2Total / iteration;
Console.WriteLine("Version\t\tTotal\t\tAvg");
Console.WriteLine("StringBuilder\t\t{0}\t\t{1}",s1Total, s1Avg);
Console.WriteLine("String.Join:\t\t{0}\t\t{1}",s2Total, s2Avg);
Console.WriteLine("Press any key..");
Console.ReadKey();
}
private static void Test(ref long s1Total, ref long s2Total, ref int iteration, int size)
{
String[] data = new String[size];
Random r = new Random();
for (int i = 0; i < size; i++)
{
data[i] = r.NextString(50);
}
Stopwatch s = new Stopwatch();
s.Start();
data.ToString1();
s.Stop();
s1Total += s.ElapsedTicks;
s.Reset();
s.Start();
data.ToString2();
s.Stop();
s2Total += s.ElapsedTicks;
iteration++;
}
Other extensions methods used in the above code for completeness..
Random extension:
public static String NextString(this Random r,int size)
{
return NextString(r,size,false);
}
public static String NextString(this Random r,int size, bool lowerCase)
{
StringBuilder sb = new StringBuilder();
char c;
for (int i = 0; i < size; i++)
{
c = Convert.ToChar(Convert.ToInt32(Math.Floor(26*r.NextDouble() + 65)));
sb.Append(c);
}
if (lowerCase) {
return sb.ToString().ToLower();
}
return sb.ToString();
}
Running the above code, my results indicate that the StringBuilder based method is faster than String.Join based method.
My Questions:
Is this the right way to be performing this type of measurement..
Is there a better way of doing this?
Are my results in this instance correct, and if so is using a StringBuilder actually faster than String.Join in this situation?
Thanks.
Next time when you want to compare the performance, you can take a look at the source code via reflector. You can easily find that string.Join is using StringBuilder to construct the string. So they have slight performance difference.
I got
StringBuilder 3428567 131867
String.Join: 1245078 47887
Note that ToString1 adds an extra newline.
Also, you can improve it by setting the StringBuilder's capacity.