[Apologies for the code at the end not wrapping nicely. Reformatting all
of those lines to get them to under 72 columns would have taken forever.]
Alvin Bruney said:
ok (long okayyyyyyyyyyyyy).
I am attaching the complete routine with a short explanation.
This routine constructs queries and returns them on an arraylist. That is
all that was timed. Another threaded routine somewhere else takes care of
the database write.
Hang on - you've got other threads running at the same time? If so, that
*completely* stuffs up the tests. You should run the tests on an otherwise
idle machine.
That routine has no knowledge of the construction
process and was not timed. For the test, the listbox had 3 items. (1249
queries repeated 3 times = 3747).
With this supplied code it would be trivial to reproduce the results. I
already said how I changed the appends to strings.
Except no, it's *not* trivial to reproduce the results, because it refers
to all kinds of variables for which we haven't the faintest idea of real
world values. However, I've done it, in a fashion - see later.
The times are in milliseconds by the way not seconds, which is a good time
IMO all things considered.
Except it's not, given the times I posted earlier, where 100,000 sets of appends
(with 50 iterations in each set, appending 3 times per iteration - in other
words, more work than it looks like is happening in your routine) took only
half a second.
I purposely stay away from contrived examples because usually these examples
in no way mimic the demands of real world applications. Therefore, and for
the most part, the results can only be used as a rough guideline, certainly
not an absolute.
So give real world examples of the variables used in your test, and we can
try to reproduce things, and improve the times.
I didn't say that microsoft is lying.
You said:
<quote>
My conclusion is: I think these microsoft guys have been feeding us a bunch
of bullshit with this stringbuilder efficiency crap.
The above is an exact quote. If someone is "feeding us a bunch of bullshit"
that says to me that they're lying.
I alluded to the fact that they may have overstated the facts (hype)
Overstating is lying.
reported results on the extremely optimistic side (long history of doing so)
etc etc. Can you find a quote of me calling them liars?
I don't think there are many people who wouldn't view "feeding us a bunch of
bullshit" as lying.
On the other hand, I remain suspicious of MS' conclusions because lately
questions have arisen which seem to not support MS' claims. Case in point,
the garbage collector DOES NOT actually return recycled memory to the
operating system. MS now claims this is a bug somewhere deep in their
framework, but it bellies the point that their claim of garbage collection
was not correct in the first place.
I don't think there's anything sinister going on - they just have a bug,
which is understandable. That's not the same situation at all as with
StringBuilder, which has been shown time and time again to be faster in
situations like yours.
I will continue to be vigilant. I still think .net is the greatest thing
since sliced bread. I am just more cautious about repeating stuff from the
microsoft marketing machine. If you think my results are completely bogus,
you are welcomed to post your times.
Kinda difficult without any sample values, isn't it? However, I've done my best -
see below.
Pardon?
Anyway, I've taken your code and bodged it around so that it'll compile, giving it
some dummy values etc.
It creates a total of 300,000 queries in 100,000 iterations (3 per iteration, just
like yours). My results are:
10000 iterations took 00:00:01.6406250
10000 iterations took 00:00:01.6250000
10000 iterations took 00:00:01.6250000
In other words, it's doing nearly 100 times as much work as your tests, in less than 10
times the time. I don't believe my computer is 10 times faster than yours - I believe
that your tests were just broken, basically. Why don't you run the code below for
yourself, and see what happens. If you get much faster times than you did before, the
natural conclusion (IMO) is that most of the time spent in your previous tests wasn't
spent running the query-building code - and thus your tests are invalidated.
Here's the code:
using System;
using System.Text;
using System.Collections;
class Test
{
// Dummy variables
string[] items = {"COPYRATE1", "COPYRATE2", "COPYRATE3"};
bool ForceCreateChecked = true;
int NEW_RATE=0;
int NEW_RATE_DATE=1;
// 2 is used directly elsewhere
int RATE=7;
int SURCHARGE=3;
int MIN_SEC=4;
int INCR_SEC=5;
int BEGIN_DATE=6;
static void Main()
{
// Repeat multiple times to allow for JITting.
for (int j=0; j < 3; j++)
{
DateTime start = DateTime.Now;
for (int i=0; i < 100000; i++)
{
new Test().SuckTheJuiceOut("newrate,newdate,1STAMERICAN,0,0,0,2150-01-01 12:00:00"+
",0,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,0,1,2");
}
DateTime end = DateTime.Now;
Console.WriteLine ("100000 iterations took {0}", end-start);
}
}
private ArrayList SuckTheJuiceOut(string databaseBound)
{
/*
if(RateDeckConfirm.Items.Count == 0)
{
Page.Controls.Add(new LiteralControl("<script>alert('An error has occurred. "+
"The target rate deck is empty. Please refill this to continue.')</script>"));
return null;
}
*/
// Not sure what this line was doing, nor why splitField2 isn't a local variable
// splitField2 = GlobalRateMan.RegexNewline.Split(databaseBound);
string[] splitField2 = databaseBound.Split ('\n');
ArrayList sql= new ArrayList();
//we build the query here, take the records out the back end, it's more efficient that way
//stuff a dummyfield to help us remove the first hidden record
StringBuilder strTempString = new StringBuilder();
// Changed GlobalRateMan.INIT_VALUE to 0 here and elsewhere; if it's not 0, I don't know what it is.
for(int index = 0; index < splitField2.Length; index++)
{
// Split by commas using just string.Split for the moment
// splitField = GlobalRateMan.RegexComma.Split(splitField2[index]);
// Again, why isn't splitField a local variable?
string[] splitField = splitField2[index].Split(',');
// Removed the "." here so I can just have a single variable
if(ForceCreateChecked)
// I don't know what GlobalRateMan.sqlUpdateActiveRate1/2 are, so I've just got literals here
strTempString.Append("Update ds_active_rate SET (rate, surcharge, "+
"min_sec, increment_sec, "+
"begin_date, new_rate, new_rate_date, location_desc, "+
"category_id, country_code, customer_number, user_name)=(");
else
strTempString.Append("Update ds_active_rate1 SET (rate, surcharge, "+
"min_sec, increment_sec, "+
"begin_date, new_rate, new_rate_date, location_desc, "+
"category_id, country_code, customer_number, user_name)=(");
//check for valid record. replace some excel formatting
if(splitField.Length == 37)
{
// Changed the location of the declarations of these variables, and put
// the assignment of data alongside the declarations. I don't know what
// FormatDate does, so I'm just going to assume we've passed in the
// right format for the moment.
string newrate = splitField[NEW_RATE];
string newdate = splitField[NEW_RATE_DATE];
//both fields must be entered or neither in the case of updates to field like surcharges
if(newrate.Length > 0 && newdate.Length > 0)
{
//rate, surcharge, min_sec, increment_sec, begin_date, new_rate, new_rate_date, user_name
//temporarily insert rate_id so we can set the where clause. remove it later
strTempString.Append("'").Append(splitField[RATE]).Append("',");
strTempString.Append("'").Append(splitField[SURCHARGE]).Append("',");
strTempString.Append("'").Append(splitField[MIN_SEC]).Append("',");
strTempString.Append("'").Append(splitField[INCR_SEC]).Append("',");
// Again, ignore FormatDate
strTempString.Append("'").Append(splitField[BEGIN_DATE]).Append("',");
// Assume GlobalRateMan.EMPTY==""
strTempString.Append("'").Append(newrate.Replace("$","")).Append("',");
strTempString.Append("'").Append(newdate).Append("',");
strTempString.Append("'").Append(InsertUserName().TrimEnd()).Append("')");
// Removed the "." here so I can just have a single variable
if(ForceCreateChecked)
{
strTempString.Append("'").Append(splitField[8] + "',");
strTempString.Append(splitField[12]).Append(",");
strTempString.Append(splitField[14]).Append(",");
strTempString.Append("'").Append(splitField[2]).Append("',");
}
}
strTempString = strTempString.Replace("',)","')");
if(newrate.Length == 0 && newdate.Length == 0)
strTempString = strTempString.Replace("new_rate,new_rate_date,","");
for(int i = 0; i < items.Length; i++)
{
// Create a new StringBuilder each time - cleaner code
StringBuilder strTempString2 = new StringBuilder();
strTempString2.Append(" where rate_deck = '").Append(items
.TrimEnd()).Append("'");
strTempString2.Append(" and location_id = ").Append(splitField[16]);
strTempString2.Append(" and world_id = ").Append(splitField[10]);
strTempString2.Append(" and rate_level = ").Append(splitField[34]);
//caught a live one
sql.Add(strTempString + strTempString2.ToString());
}
// I don't think any of this is needed
// strTempString.Length = GlobalRateMan.INITVALUE;
// newrate = newdate = "";
}
}
return sql;
}
string InsertUserName()
{
return "jon";
}
}