skip to Main Content

My Article on Dotnetslackers.com: Bulk Operations Using Oracle Data Provider for .NET (ODP.NET)

My first article for Dotnetslackers.com is up. Here’s an excerpt:

In a typical multi-tier application, one of the biggest performance bottlenecks is the overhead of making round-trips to the database. Minimizing these round-trips is often the first area you should look at during performance tuning. Fortunately, the Oracle Data Provider for .NET (ODP.NET) makes it fairly easy to do this by providing several built-in methods to write and read data in bulk.

You can view the article here.

Finds of the Week – 10/24/2007

.NET

  • Improve ODP.NET Performance (Mark A Williams, via Oracle Magazine) shows how Connection Pooling, Controlling Fetch Size, and Statement Caching can be used to maximize performance with the Oracle Data Provider for .NET.
  • Did you know that you can profile a selected Unit Test in Visual Studio Team System? Read more at Profiling a Unit Test (Noah Coad, via blogs.msdn.com).

Tools

  • Sysinternals (now part of Microsoft) has released a new version (v11.02) of their extremely useful Process Explorer utility. This is one of my must-have utility on any new system.
  • Process Explorer

  • FileZilla is an open source FTP utility for Windows. It's free and has all the features I use. What more can one ask for?
  • Filezilla

  • I signed up for Mozy two weeks ago. The price is right: $4.95 per month for peace of mind. However, it’s been 2 weeks and the initial backup is still only 50% complete!! I have a Verizon FIOS connection with 2000 Kbps upload speed and about 37GB of data to backup. I’ll post an update on how restores work whenever this thing finishes.
  • Mozy

Windows Mobile / Pocket PC

  • Google has just announced free IMAP for Gmail! Unfortunately, it's not available on my Gmail account yet. I am hoping that the IMAP with Windows Mobile Outlook is better than Gmail mobile (via Pocket IE or Opera Mobile).

Blogs

  • Scott Hanselman's Computer Zen is one of my favorite .NET related blogs. I don't know how he does it, but Scott often writes several detailed posts a day. Don't forget to check out his Tools List post.

Miscellaneous

  • Did you know that the best Wikipedia content is often from anonymous "good samaritans"? "Anonymous contributors with a single edit had the highest quality of any group." Read more here.

And Now, Something Different

Alexa.com's current list of 100 Top Sites in the United States (sorted by traffic) has a couple of interesting entries. In addition to the familiar Yahoo!, Google, and Myspace, there are two Vietnamese related sites: VnExpress at number 70, and Vietfun, at number 80. Besides these two sites, there are no other non-English or minority-centered web sites on the list that I could tell.

Possible explanations:

  • Alexa rankings are way off base.
  • Vietnamese Americans are using the net much more than any other minority groups (unlikely). If this is true, there is an untapped market here.

Anyone else has an explanation?

alexa-rankings-usa-1

alexa-rankings-usa-1

StringBuilder is not always faster – Part 2 of 2

In a previous article (“StringBuilder is not always faster), I provided some quick benchmark data and gave “rules of thumb” for when to use StringBuilder and when to use traditional string concatenations. In this follow-up article, I will attempt to provide a more detailed analysis.

If you don’t want to bother with the details, jump directly to the conclusions here.

A Look at the Generated MSIL

A reader, Matt, suggested that it’s possible the compiler may have noticed that I never use the generated test objects in my benchmark code and not created them. That would definitely invalidated my test results!

Here’s the original benchmark code:

for (int i = 0; i <= 1000000; i++)
{
    // Concat strings 3 times using StringBuilder
    StringBuilder s = new StringBuilder();
    s.Append(i.ToString());
    s.Append(i.ToString());
    s.Append(i.ToString());
}

And this one, using traditional concatenation, took slightly less time (1344 milliseconds):

for (int i = 0; i <= 1000000; i++)
{
    // Concat strings 3 times using traditional concatenation
    string s = i.ToString();
    s = s + i.ToString();
    s = s + i.ToString();
}

According to Lutz Roeder’s .NET Reflector (great tool), the answer is no. Here’s the IL from Reflector:

.entrypoint
.maxstack 2
.locals init (
    [0] int32 i,
    [1] class [mscorlib]System.Text.StringBuilder s,
    [2] string V_2,
    [3] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: br.s L_003b
L_0005: nop
L_0006: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
L_000b: stloc.1
L_000c: ldloc.1
L_000d: ldloca.s i
L_000f: call instance string [mscorlib]System.Int32::ToString()
L_0014: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
L_0019: pop
L_001a: ldloc.1
L_001b: ldloca.s i
L_001d: call instance string [mscorlib]System.Int32::ToString()
L_0022: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
L_0027: pop
L_0028: ldloc.1
L_0029: ldloca.s i
L_002b: call instance string [mscorlib]System.Int32::ToString()
L_0030: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
L_0035: pop
L_0036: nop
L_0037: ldloc.0
L_0038: ldc.i4.1
L_0039: add
L_003a: stloc.0
L_003b: ldloc.0
L_003c: ldc.i4 0xf4240
L_0041: cgt
L_0043: ldc.i4.0
L_0044: ceq
L_0046: stloc.3
L_0047: ldloc.3
L_0048: brtrue.s L_0005
L_004a: ldc.i4.0
L_004b: stloc.0
L_004c: br.s L_0078
L_004e: nop
L_004f: ldloca.s i
L_0051: call instance string [mscorlib]System.Int32::ToString()
L_0056: stloc.2
L_0057: ldloc.2
L_0058: ldloca.s i
L_005a: call instance string [mscorlib]System.Int32::ToString()
L_005f: call string [mscorlib]System.String::Concat(string, string)
L_0064: stloc.2
L_0065: ldloc.2
L_0066: ldloca.s i
L_0068: call instance string [mscorlib]System.Int32::ToString()
L_006d: call string [mscorlib]System.String::Concat(string, string)
L_0072: stloc.2
L_0073: nop
L_0074: ldloc.0
L_0075: ldc.i4.1
L_0076: add
L_0077: stloc.0
L_0078: ldloc.0
L_0079: ldc.i4 0xf4240
L_007e: cgt
L_0080: ldc.i4.0
L_0081: ceq
L_0083: stloc.3
L_0084: ldloc.3
L_0085: brtrue.s L_004e
L_0087: ret

Size of Concatenated Values and StringBuilder with Initial Capacity

More questions from Matt: how does the performance curve change if the concatenated values are larger? And what if you seed the StringBuilder object with an initial capacity?

To answer the questions, I wrote some more benchmarks (complete source code at the bottom of this article) to compare three different methods of concatenation:

  • “+” concatenation: This is the traditional a + b + c method.
  • StringBuilder: Create a StringBuilder object using the default constructor and calling Append().
  • StringBuilder/Pre-allocated: Create a StringBuilder object and preallocate the initial capacity so that it does not need to expand later.

String Size = 10

The chart below shows elapsed times (ms) of the three concatenation methods, with the concatenated string size equaled to 10 characters

StringBuilder Benchmark - Elapsed time vs Concatenations - String Size 10

As the chart illustrates, when the size of the concatenated value is small (10 characters in this test), “+” concatenation (blue line) performs faster than StringBuilder until the number of concatenations reaches 6. After 6, StringBuilder starts to work exponentially faster.

However, when compared with the StringBuilder/Pre-allocated method, StringBuilder starts to perform as fast as “+” concatenation much earlier: at 3 concatenations.

Note: The orange line for StringBuilder is not very linear. My guess is that it’s due to the need to allocate space as needed. The memory allocation itself will consume CPU cycles. The default StringBuilder constructor will allowcate 16 bytes initially. Thereafter, it will allocate two times the current capacity whenever needed.

String Size = 100

StringBuilder Benchmark - Elapsed time vs Concatenations - String Size 100

When the concatenated value is 100 characters, the all three methods perform very similarly up to three concatenations, then StringBuilder/Pre-allocated pulls ahead at 4 concatenations.

Concatenated String Size = 1000

StringBuilder Benchmark - Elapsed time vs Concatenations - String Size 1000

At 1000 characters, things begine a little bit more interesting: StringBuilder/Pre-allocated is faster in all cases (although the difference is very small until about 6 concatenations). Since it may not be always possible or practical to know the final string size ahead of time, for this graph, I also added two more series to show what happens if you over-estimate or under-estimate the final capacity. As expected, there is a performance penalty for both. The more inaccurate your estimated capacity is, the higher of a performance penalty you will get.

What about String.Concat?

Lars Wilhelmsen asked “What about string.Concat?” According to my research, string.Concat is basically identical to “+” used on a single line.

This:

string s = "a" + 
                    class="str">"b" + "c";

Is the same (same generated IL) as this:

string s = string.Concat(
 class="str">"a", "b", "c");

But not this:

string s = "a";
s = s + "b";
s = s + "c";

Remember, the “+” must be on the same logical line, otherwise, the compiler will convert each line into a separate string.Concat operation, resulting in slower performance.

AJ has written a post detailing string.Concat here. Thanks, AJ, for pointing it out.

And String.Format?

Flyswat wanted to know about string.Format. I did some quick benchmark code again. The string.Format code below took 58 milliseconds to run 100,000 iterations:

string s = string.Format(
          class="str">"Value: {0}", strVal);

While this code, using “+” concatenation, only took 9 milliseconds (also 100,000 iterations):

string s = "Value: " + strVal;

According to the above numbers, string.Format is significantly slower than “+” concatenations. The difference in speed is similar between s.AppendFormat(“Value: {0}”, strVal) and s.Append(“Value: ” + strVal). I have used String.Format a lot in my code and I have not thought about this performance penalty. It does make sense. String.Format (or StringBuilder.AppendFormat) has to scan the string looking for format specifiers… that takes time. String.Format is very useful to make the code easier to read or when you actually need to format numbers. Even with this new data, I will not neccessarily shy away from using string.Format. I will however definitely be much more observant when using it, especially when used inside a loop or performance critical code path.

Conclusions

The new benchmarks do point to StringBuilder/Pre-allocated as the fastest method regardless of number of concatenations, when the concatenated string value is large (1000 characters).

With that in mind, here are my slightly modified rules of thumbs for string concatenation. Remember, “rules of thumb” are short statements to provide general princicles, and may not be accurate for every single situation. For performance criticial code, you should consider running some benchmarks/profiling yourself.

  • For 1-4 dynamic concatenations, use traditional “+” concatenation.
  • For 5 or more dynamic concatenations, use StringBuilder.
  • When using StringBuilder, try to provide the starting capacity as close to the final string size as possible.
  • When building a big string from several string literals, use either the @ string literal or the + operator.
  • For performance critical code, consider running your own benchmarks

Additional Reading

Notes

The benchmarks in this article were run on a Pentium 4 2.4 GHz CPU, with 2GB of RAM. With .NET framework: 2.0.

Source Code for Benchmarks

Source code here.

kick it on DotNetKicks.com

It’s OK to Be Lazy

At least when it comes to instantiating objects.

Even in today’s environment, when the typical amount of RAM on each server is in the gigabytes, it’s still wise to pay attention to memory usage. As a developer or architect, you need to be aware of the trade-offs between eager instantiation and lazy instantiation. Yes, it’s rather pointless to consider an Int16 versus an Int32 for a variable if it’s just going to be created and used a few times in the lifetime of your application. However, if that same variable is instantiated thousands of times or more, then the potential improvement in either memory usage or performance (whichever is more important to you) is definitely worth a look.

Eager/Lazy Instantiation Defined

With eager instantiation, the object is created as soon as possible:

Example – Eager Instantiation

public class Customer
{
    // eager instantiation
    private Address homeAddress = new Address();
    public Address HomeAddress
    {
        get
        {
             return homeAddress;
        }
    }
}

With lazy instantiation, the object is created as late as possible:

Example – Lazy Instantiation

public class Customer
{
   private Address homeAddress;
   public Address HomeAddress
   {
       get
       {
           // Create homeAddress if it’s not already created
           if (homeAddress == null)
           {
               homeAddress = new Address();
           }
           return homeAddress;
        }
    }
}

Eager/lazy instantiation also applies to classes, singletons, etc. The principles and potential advantages/disadvantages are similar. For this article, I am only discussing the instantiation of class members.

CPU Cycles vs. Memory Usage

Eager vs. lazy instantiation is the classic performance/memory trade-off. With eager instantiation, you gain some performance improvement at the cost of system memory. Exactly what kind of performance/memory trade-off are we talking about? The answer depends mostly on the objects themselves:

  • How many instances of the parent object do you need?
  • What is the memory footprint of the member object?
  • How much time does it take to instantiate the member object?
  • How often will the parent object/member object be accessed?

Calculating the Memory Footprint of an Object

According to my own experiments (using DevPartner Studio and .NET Memory Profiler), each reference-type object (class) has a minimum memory footprint of 12 bytes. To calculate the total memory footprint of each reference-type object, add up any other memory used by members in the object. To get the exact memory footprint, you also need to take into consideration “boundaries” but for our purpose that’s probably not important.

The memory foot-print of an object can be closely approximated using the following table (from MSDN Magazine):

Type Managed Size in Bytes
System.Boolean 1
System.Byte 1
System.Char 2
System.Decimal 16
System.Double 8
System.Single 4
System.Int16 2
System.Int32 4
System.Int64 8
System.SByte 1
System.UInt16 2
System.UInt32 4
System.UInt64 8

Using the example Customer class above, let’s say that each Address object take up 1 KByte, and my application frequently needs to instantiate up to 10,000 Customer objects. Just by creating 10,000 Customer objects, we would need about 10 Megabytes of memory. Now let’s say that the HomeAddress member is only needed when the user drills down into the details of a Customer, and we are looking at a potential saving of 10 Megabytes of memory by using lazy instantiation on HomeAddress.

Memory Usage Can Also Impact Performance

Another important consideration with .NET managed code is garbage collection. In .NET managed code, memory usage has a hidden impact on performance in terms of the work the garbage collector has to perform to recover memory. The more memory you allocate and throw away, the more CPU cycles the garbage collector has to go through.

Recommendations

  • Pay closer attention to classes that get instantiated multiple times, such as Orders, OrderItems, etc.
  • For light-weight objects, or if you are not sure, use lazy instantiation.
  • If a member object is only used some of the times, use lazy instantiation.

Additional Reading

Rediscover the Lost Art of Memory Optimization in Your Managed Code

A New Way to Measure Lines of Code

Is Lines of Code a good way to measure programmer output?

Background

First, some background: several studies (Sackman, Erikson, and Grant – 1968; Curtis – 1981) have shown that there are large variations in productivity levels among the best and worst programmers. While the numbers from the studies are controversial, I tend to agree with the basic premise that a super programmer can significantly outperform the average programmer. In my real-world projects, I estimate that variations have ranged up to 5/1.

As a manager or technical lead of a project, it’s important to have a good idea of how productive your programmers are. With a good idea of productivity levels, you can make better estimates for time and resources, and you can manage the individual developers better. Knowing that Programmer A has relatively lower productivity than his teammates, you can assign him smaller features and save the more complex ones for more productive/better programmers. Or, in the case of the negative-productivity programmer, you can identify him quickly and react appropriately instead of letting him continue to negatively impact your project.

So, is Lines of Code (LOC) per Day by itself a good way to measure productivity? I think the answer is a resounding no for many reasons:

  • A good programmer is able to implement the same feature with much less code than the average programmer.
  • Code quality is not taken into account. If you can write a thousand lines of code more than the average programmer, but your code is twice as buggy, that’s not really desirable.
  • Deleting and changing code, activities that are associated with important tasks such as re-factoring and bug-fixing, are not counted, or even counted negatively.

A New Method to Measure LOC

If LOC is not a good way to measure productivity, why am I writing about it? Because it’s still a good metric to have at your disposal, if you use it correctly, carefully, and in conjunction with other data. I also propose a revised method to calculate LOC that can better correlate with productivity. This “new-and-improved” LOC, in conjunction with other data (such as a Tech Lead’s intimate knowledge of his programmers’ style, efficiency, and skill level), may allow us to gain a better picture of programmer productivity.

The traditional way of calculating LOC has always been to count the lines of source code added. There are variations, such as not counting comments or counting statements instead of lines, but the general concept is the same: only lines or statements of code that are added are counted. The problems with the old method are:

  • Buggy code counts as much as correct code.
  • Deleting or changing code is not counted. Deleting/changing code is often done when you are re-factoring, or fixing bugs.
  • Optimizing a 20,000-line module to make it 10,000 lines actually impacts the LOC negatively.

At a conceptual level, my new method to calculate LOC (let’s call it “Lines of Correct Code” or LOCC) only counts correct lines of code, and code that is deleted or changed. Short of reviewing each line of code manually, how does a program know if a line of code is correct? My answer: if it remains in the code base at the end of the produce cycle, then for our purpose, it is “correct” code.

Algorithm for Counting Lines of Correct Code

Below is the proposed algorithm for calculating the LOCC. It should be possible to automate every of the steps described here using a modern source control system.

  • Analyze the source code at the end of the product cycle and keep a picture of the code that exists at the end. This is our base-line “correct” code.
  • Go back to the beginning of the project cycle and examine each check-in. For each check-in, count the lines of code that is added or changed and remains until the end. Lines of code that are deleted are also counted.
  • Auto-generated code is not counted or is weighted appropriately (after all, some work is involved).
  • Duplicate files are only counted once. In many applications, some files are mirrored (shared in SourceSafe-speak) in multiple locations. It’s only fair to count these files only once.

Ways to Use Lines of Correct Code

Here are a few ways I am planning to use LOCC in my projects:

  • Look at the LOCC per day (week/month) of the same developer over time.
  • Compare the LOCC per day between different programmers of equal efficiency and skill level.
  • Compare the total LOCC between different projects to get an idea of their relative size.
  • Correlate the LOCC of a programmer against his/her bug rate.
  • If a programmer writes code that is often deleted or changed later on, try to find out why.

Tell me what you think. Is this LOCC metric something that you would consider using in your project? I am writing a utility to calculate LOCC automatically from SourceSafe and if there’s sufficient interest, I will consider making it available.

StringBuilder is not always faster – Part 1 of 2

How often have you been told to use StringBuilder to concatenate strings in .NET? My guess is often enough. Here is something you may not know about string concatenation: StringBuilder is not always faster. There are already many articles out there that explain the why’s, I am not going to do that here. But I do have some test data for you.

When concatenating three values or less, traditional concatenation is faster (by a very small margin)

This block of code took 1484 milliseconds to run on my PC:

for (int i = 0; i <= 1000000; i++) 
{ 
    // Concat strings 3 times using StringBuilder 
    StringBuilder s = new StringBuilder(); 
    s.Append(i.ToString()); 
    s.Append(i.ToString()); 
    s.Append(i.ToString()); 
}

And this one, using traditional concatenation, took slightly less time (1344 milliseconds):

for (int i = 0; i <= 1000000; i++) 
{ 
    // Concat strings 3 times using traditional concatenation 
    string s = i.ToString(); 
    s = s + i.ToString(); 
    s = s + i.ToString(); 
}

The above data suggests that StringBuilder only starts to work faster once the number of concatenations exceed 3.

Building strings from literals

When building a large string from several string literals (such as building a SQL block, or a client side javascript block), use neither traditional concatenation nor StringBuilder. Instead, choose one of the methods below:

+ operator

// Build script block 
string s = "<script>" 
       + "function test() {" 
       + "  alert('this is a test');" 
       + "  return 0;" 
       + "}";

The compiler concatenates that at compile time. At run-time, that works as fast as a big string literal.

@ string literal

I sometimes use the @ string literal which allows for newlines (I find this syntax is harder to maintain, formatting-wise, than using the + operator):

string s = @"<script> 
        function test() { 
        alert('this is a test'); 
        return 0; 
        }";

Both methods above run about 40 times faster than using StringBuilder or traditional string concatenation.

Rules of Thumb

  • When concatenating three dynamic string values or less, use traditional string concatenation.
  • When concatenating more than three dynamic string values, use StringBuilder.
  • When building a big string from several string literals, use either the @ string literal or the inline + operator.

Updated 2007-09-29

I have posted a follow-up article to provide more detailed analysis and to answer some of the questions asked by readers.

Stop Your .NET Managed Memory from Taking a Leak

It’s been a while since my last post… I have been very busy with wrapping the latest development project at work and I also took a 3-week long vacation to Vietnam in December! That was nice. I am still having post-vacation depression syndrome. When I have more time I will write more about the trip to Vietnam and maybe post some pictures.

Anyway, this post is about .NET memory leaks. If you suspect a memory leak in your .NET managed code, I recommend downloading a tool called .NET Memory Profiler. In my last project, we had a huge managed memory leak in our app due to the incorrect use of static events. This tool was a life saver because it helped us track down the memory leak.

Back To Top