5

Here are two code parts in c++ and c# doing absolutely the same thing:

C++
http://ideone.com/UfL5R

#include <stdio.h>
int main(int argc, char *argv[]) {
  char p[1000000];
  unsigned int i,j;
  unsigned long long s=0;
  for(i=2;i<1000000;i++) p[i]=1;
  for(i=2;i<500000;) {
    for(j=2*i;j<1000000;j+=i) p[j]=0;
    for(i++;!p[i];i++);
  }
  for(i=3,s=2;i<1000000;i+=2) if(p[i]) s+=i;
  printf ("%lld\n",s);
  return 0;
}

time: 0.01s memmory: 2576 kB

C#
http://ideone.com/baXYm

using System;

namespace ConsoleApplication4
{
    internal class Program
    {
        private  static void Main(string[] args)
        {
            var p = new byte[1000000];
            ulong i, j;
            double s = 0;
            for(i=2;i<1000000;i++) 
                p[i]=1;

            for(i=2;i<500000;) 
            {
                for(j=2*i;j<1000000;j+=i) 
                    p[j]=0;
                for(i++;p[i]==0;i++);
            }

            for(i=3,s=2;i<1000000;i+=2) 
                if(p[i]!=0) s+=i;

            Console.WriteLine(s);
        }
    }
}

time: 0.05s mem: 38288 kB

How can I improve the C# code to prove that C# can be as fast as C++ to my colleague?

As you can see the C# execution time is 5 time larger, and the memory consumption is 15 times larger.

Lance Roberts
  • 22,383
  • 32
  • 112
  • 130
v00d00
  • 3,215
  • 3
  • 32
  • 43
  • 7
    It's probably worth noting some stuff before delving into it: your arrays are different. The array in the C/C++ example is on the stack. In C#, it's going on the heap. Your `i` and `j` variables in C# are larger footprints, and may require more effort for the processor to use if it's a 32-bit processor. Use `uint`, assuming (fairly considering the iteration amount) `unsigned int` is 4 bytes in the C/C++ example. `p[0]` and `p[1]` will be uninitialzed and thus questionable in the C/C++ example, but `0` in the C# example. – pickypg May 21 '11 at 23:47
  • 1
    Plus `s` is a `double` in the C# example and a `ulong` (effectively) in the C/C++ example. Integer arithmetic will practically always be faster than floating point. – pickypg May 21 '11 at 23:53
  • 1
    The "C++ code example" of yours is written in pure C. So before proving anything it might help to learn something first. – Öö Tiib May 21 '11 at 23:54
  • 1
    You didn't time the *printing*, did you? That doesn't really count. :P – user541686 May 21 '11 at 23:54
  • 3
    @pickypg: not to mention *less error-prone*. @Dimitry: as you can see the two are not doing "*absolutely* the same thing", only similar things. – R. Martinho Fernandes May 21 '11 at 23:54

5 Answers5

10

Compile and run in Release mode. I get exactly 0.01s from the C# version when built and run in Release mode. As far as memory consumption is concerned you are comparing apples to oranges. A managed environment will consume more memory as it is hosting the CLR and the Garbage Collector which don't come without cost.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Measurments are done by the Ideone server. If it measures include runtime used memmory, than it becomes clear where the additional 32 Mb memmory comes from. – v00d00 May 22 '11 at 00:05
  • 1
    I get your meaning, but I'd still consider it apples to apples. In @both cases, you're comparing the memory consumption of the entire process. That's fair. .NET users more memory, and a comparison of memory usage ought to reflect that. – jalf May 22 '11 at 00:07
6

How can I improve the C# code to prove that C# can be as fast as C++ to my colleague?

You can't. There are legitimate areas where C++ is fundamentally faster than C#. But there are also areas where C# code will perform better than the equivalent C++ code. They're different languages with different strengths and weaknesses.

But as a programmer, you really ought to base your decisions in logic.

Logic dictates that you should gather information first, and then decide based on that.

You, on the contrary, made the decision first, and then looked for information to support it. That may work if you're a politician, but it's not a good way to write software.

Don't go hunting for proof that C# is faster than C++. Instead, examine which option is faster in your case.

In any case, if you want to prove that X can be as fast as Y, you have to do it the usual way: make X as fast as Y. And as always, when doing performance tuning, a profiler is your best friend. Find out exactly where the additional time is being spent, and then figure out how to eliminate it.

Memory usage is a lost cause though. .NET simply uses more memory, for several reasons:

  • it has a bigger runtime library which must be present in the process' address space
  • .NET objects have additional members not present in C++ classes, so they use more memory
  • the garbage collector means that you'll generally have some amount of "no-longer-used-but-not-yet-reclaimed" memory lying around. In C++, memory is typically released immediately. In .NET it isn't. .NET is based on the assumption that memory is cheap (which is typically true)
jalf
  • 243,077
  • 51
  • 345
  • 550
6

How to GREATLY Increase the Performance of your C# Code

Go "unsafe" (unmanaged) for that... every time you're doing someSortOfArray[i], the .NET framework is doing all kinds of neat-o things (such as out of bounds checking) which take up time.

That's really the whole point of going unmanaged (and then using pointers and doing myPointer++).

Just to clarify, if you go unmanaged and then still do a for-loop and do someArray[i], you've saved nothing.

Another S.O. question that may help you: True Unsafe Code Performance

Disclaimer

By the way, I'm not saying to do this all the time, but rather as an answer for THIS specific question only.

Community
  • 1
  • 1
Timothy Khouri
  • 31,315
  • 21
  • 88
  • 128
  • 3
    The out of bounds checks can be skipped by the JIT because it never goes out of bounds. – R. Martinho Fernandes May 21 '11 at 23:57
  • Can I allocate 1 mb array on stack? And get data from there? – v00d00 May 22 '11 at 00:00
  • @Dmitry: there's [`stackalloc`](http://msdn.microsoft.com/en-us/library/cx9s2sy4.aspx) (probably the only place in the C# spec that mentions this "stack" thingy people keep talking about :). If there is one meg available on the stack, you can. – R. Martinho Fernandes May 22 '11 at 00:18
  • 1
    @Martinho - the 32 bit jitter only eliminates the bounds check under certain circumstances. For example, if you store the array's length in a local variable and then check the loop variable against that, you are going to be doing bounds checking. The 64-bit jitter behaves differently in this regard (with less optimization). (This comes from an article I read several months ago that I unfortunately can't find at the moment.) – phoog May 22 '11 at 02:58
3

Just a note to your timing. Its not shown, how did you measure the execution times. One can expect a reasonable overhead for .NET applications on startup. So if you are about the execution time of the loops only, you should run the inner loops several (many) times, skip the 1..2 first iterations, measure the other iterations and compute the average.

I would expect the results be more similar than. However, as always when targeting 'peak performance' - precautions regarding the memory management are important. Here, it probably would be sufficient to prevent from 'new' inside the measurement functions. Reuse the p[] in each iteration.

Haymo Kutschbach
  • 3,322
  • 1
  • 17
  • 25
1

The memory usage may be related to garbage collection. In Java, memory usage is intentionally high -- garbage collection only happens when you need more memory. This is for speed reasons, so it would make sense that C# does the same thing. You shouldn't do this in release code, but to show much memory you're actually using, you can call GC.Collect() before measuring memory usage. Do you really care how much memory it's using though? It seems like speed in more important. And if you have memory limits, you can probably set the amount of memory that your program will use before garbage collecting.

Brendan Long
  • 53,280
  • 21
  • 146
  • 188