Interesting Perf Lesson

Tyler, an old friend and an outstanding contractor for VelocIT recently wrote a post suggesting one would receive better performance by passing in an array of objects to the Microsoft Data Application Block methods rather than passing in an array of SqlParameter instances. He cited this article.

The article suggests that instead of this:

public void GetWithSqlParams(SystemUser aUser)
{
  SqlParameter[] parameters = new SqlParameter[]
  {
    new SqlParameter("@id", aUser.Id)
    , new SqlParameter("@name", aUser.Name)
    , new SqlParameter("@name", aUser.Email)
    , new SqlParameter("@name", aUser.LastLogin)
    , new SqlParameter("@name", aUser.LastLogOut)
  };

  SqlHelper.ExecuteNonQuery(Settings.ConnectionString
    , CommandType.StoredProcedure
    , "User_Update"
    , parameters);
}

You should do something like this for performance reasons:

public void GetWithSqlParams(SystemUser aUser)
{
  SqlHelper.ExecuteNonQuery(Settings.ConnectionString
    , CommandType.StoredProcedure
    , "User_Update"
    , aUser.Id
    , aUser.Name
    , aUser.Email
    , aUser.LastLogin
    , aUser.LastLogout);
}

Naturally, when given such advice, I fall back to the first rule of good performance from the perf guru himself, Rico Mariani. Rule #1 Is to Measure. So I mentioned to Tyler that I’d love to see metrics on both approaches. He posted the result on his blog.

Calling the methods included in a previous post, 5000 times each,

With parameters took 1203.125 milliseconds.
With objects took 1250 milliseconds. 
Objects took -46.875 milliseconds less.

20000 times each:

With parameters took 4859.375 milliseconds.  
With objects took 5015.625 milliseconds.
Objects took -156.25 milliseconds less.

The results show that the performance difference is negligible. However, even before seeing the performance results, I would agree with the article to choose the second approach, but for different reasons. It results in a lot less code. As I have said before, Less code is better code.

I tend to prefer optimizing for productivity all the time, but only optimizing for performance after carefully measuring for bottlenecks.

There’s also a basic economics question hidden in this story. The first approach does seem to eke out slightly better performance, but at what cost? That’s a lot more code to write to eke out 47 milliseconds worth of performance out of 5000 method calls. Is it really worth it?

This particular example may not be the best example of this principle of wasting time optimizing at the expense of productivity because there is one redeeming factor worth mentioning with the first approach.

By explicitly specifying the parameters, the parameters can be listed in any order, whereas the second approach requires that the parameters be listed in the same order that they are specified in the stored procedure. Based on that, some may find the first approach preferable. Me, I prefer the second approach because it is cleaner, easier to read, and I don’t see keeping the order intact much more work than getting the parameter names correct.

But that’s just me.

What others have said

Requesting Gravatar... Scott Muc Oct 19, 2006 3:55 AM
# re: Interesting Perf Lesson
How does the object array deal with parameterize queries? The order that the parameters appear in the sql text?

Also, using a SqlParameter can be helpful when performing INSERTs to clip large text entries. eg:

new SqlParameter("@Input", SqlDbType.VarChar, 80)

Albeit it's debatable as to whether or not that kind of input error should have been caught earlier, but at least with that SqlParameter you can guarantee that the parameter is less than or equal to whatever specified maximum length of the database column is.
Requesting Gravatar... Eric Appel Oct 19, 2006 5:28 AM
# re: Interesting Perf Lesson
Isn't the second approach more likely to open you up to sql injection attacks?
Requesting Gravatar... Scott Muc Oct 19, 2006 5:46 AM
# re: Interesting Perf Lesson
Eric, that's the whole point of paramaterized queries.

It allows you to safely write code like the following:

string sql = "SELECT * FROM Users WHERE UserId = @UserId";
SqlParameter paramUserId = new SqlParameter("UserId", SqlDbType.Int);
paramUserId.Value = 1;
// I like to use the SqlHelper class

IDataReader reader = SqlHelper.ExecuteReader(ConnectionString, CommandType.Text, sql, paramUserId);

Firstly, this parameter ensures that the paramater is an INT so a string value would throw an exception here. Also, if we were using a VARCHAR parameter, the SqlParameter value assignment automatically escapes the string for us.

If you run SQL Profiler and observe the queries, you'll notice that they are actually execute via a Stored Procedure (sp_executesql)

Hope that clears that up a bit.
Requesting Gravatar... Eric Appel Oct 19, 2006 5:57 AM
# re: Interesting Perf Lesson
If you do this:

public void GetWithSqlParams(SystemUser aUser)
{
SqlHelper.ExecuteNonQuery(Settings.ConnectionString
, CommandType.StoredProcedure
, "User_Update"
, aUser.Id
, aUser.Name
, aUser.Email
, aUser.LastLogin
, aUser.LastLogout);
}

then you aren't actually creating SqlParameter objects, correct? I wasn't sure if SqlHelper.ExecuteNonQuery() parameter list actually created SqlParameter objects for you when passing in what looks to be string types. Maybe I am missing something.
Requesting Gravatar... Haacked Oct 19, 2006 6:06 AM
# re: Interesting Perf Lesson
The article makes it clear what's happening under the hood is that the Microsoft Data Application Block is creating the SqlParameter instances based on the stored procedure.

So you aren't necessarily opening yourself up to SQL Injection attacks as long as you are using parameterized queries or stored procedures.
Requesting Gravatar... AsbjornM Oct 19, 2006 6:52 AM
# re: Interesting Perf Lesson
I don't care about which is faster, especially then the difference is so low.
However, I DO care about readability and maintanability, and I cannot say that your last query is more readable than the former one. (I think you made an typo there though :) )
Anyway, If you are concerned about "getting the parameter names correct", then, why are you not concerned about getting them in right order?, What if you have optional parameters?

Other takes on this could be interresting, like, using SubSonic or equal tools to generate sp wrapper code, so you could instead use strongly typed parameters when calling an given sp, that I think would be even better, and then I don't care about how things are done within that method, since it should always be in sync with the sp in the database (given that no-one is changing the database without notifying the developer)

What do you have to say?

(will show your gravatar)
Please add 7 and 8 and type the answer here: