2

Edit: I will try to specify my question better. I'm not asking about how to measure the performance or anything or how to write the algorithm. I tried to mirror the source of the java application into C#. The java codes runs about 1.3 - 2.0 times faster than the C# code. So why is it like that? Did i make a mistake in porting the code? Are the threads of the ExecutorService in java the same as the c# tasks? Why is even java singlethreaded faster than c# multithreaded ( java multithreaded is the fastest )?

For testing purposes (getting back into the languages) I coded a pseudo brute force application in Java and later ported it to C#. I tried to change the least possible so that both sources stay the same semantically. I know that these pieces of code aren't perfect and please don't try to correct the algorithm behind it as this is not related to the question.

The Question: So when I run both applications consecutively and then compare the output, Java is faster in every try and Java singlethreaded is almost as fast or faster than C# multithreaded. I want to know why it is like that and maybe what I did wrong (if anything) in the C# version of the code. Any hints on fatal coding mistakes? Also, on the Windows XP configuration C# multithreaded is slower than C# singlethreaded (how is this even possible?).

Tried it on 2 configurations:

1)
Windows 7 x64
i7 cpu, 8 cores ( 4 physical cores + Hyperthreading )
.Net 4.0 and jdk 7

2)
Windows XP x86
Atom N270, 2 cores ( 1 physical core + Hyperthreading )
.Net 4.0 and jdk 7

I'm posting the code so you can test yourself.

Java Code:

Entry.java

package test;
import java.util.Scanner;

public class Entry
{
    public static void main(String[] args)
    {

    System.out.print("Type password to be cracked: ");
    String input = new Scanner(System.in).nextLine();
    PasswordCracker cracker = new PasswordCracker();
    System.out.println("Multithreaded");
    cracker.runMulti(input);
    cracker = new PasswordCracker();
    System.out.println("Singlethreaded");
    cracker.runSingle(input);
    System.out.println("Finished...");
    }
}

PasswordCracker.java

package test;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class PasswordCracker
{

String passwordToCrack;
public boolean passwordFound;
int min;
int max;
StringBuilder crackedPassword;

public void prepare(String text)
{
    passwordToCrack = text;

    passwordFound = false;
    min = 32;
    max = 126;
    crackedPassword = new StringBuilder();
    crackedPassword.append((char) (min - 1));
}

public void result()
{
    System.out.println("Cracked Password is: " + crackedPassword.toString());
}

public void incrementString(StringBuilder text, int min, int max)
{
    text.setCharAt(0, (char) ((int) text.charAt(0) + 1));
    for (int i = 0; i < text.length(); i++)
    {
        if (text.charAt(i) > (char) max)
        {
            text.setCharAt(i, (char) min);
            if (text.length() == i + 1)
            {
                text.append((char) min);
            }
            else
            {
                text.setCharAt(i + 1, (char) ((int) text.charAt(i + 1) + 1));
            }
        }
    }
}

public void runMulti(String text)
{
    prepare(text);
    double time = System.nanoTime();
    doItMulti();
    time = System.nanoTime() - time;
    System.out.println(time / (1000000000));
    result();

}

public void runSingle(String text)
{
    prepare(text);
    double time = System.nanoTime();
    doItSingle();
    time = System.nanoTime() - time;
    System.out.println(time / (1000000000));
    result();
}

public void doItSingle()
{
    while (passwordFound == false)
    {
        incrementString(crackedPassword, min, max);
        passwordFound = crackedPassword.toString().equals(passwordToCrack);
    }
}

public void doItMulti()
{
    int cores = Runtime.getRuntime().availableProcessors();
    ArrayList<Future<?>> tasks = new ArrayList<Future<?>>(cores);
    ExecutorService executor = Executors.newFixedThreadPool(cores);
    final long step = 2000;
    for (long i = 0; i < Long.MAX_VALUE; i += step)
    {
        while(tasks.size() > cores)
        {
            for(int w = 0; w < tasks.size();w++)
            {
                if(tasks.get(w).isDone())
                {
                    tasks.remove(w);
                    break;
                }
            }
            try
            {
                Thread.sleep(0);
            }
            catch (InterruptedException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        {
            final long j = i;
            if (passwordFound == false)
            {
                tasks.add(executor.submit(new Runnable()
                {

                    public void run()
                    {
                        long border = j + step;
                        StringBuilder toCrack = new StringBuilder(10);
                        toCrack.append(constructString3(j, min, max));
                        for (long k = j; k < border; k++)
                        {
                            incrementString(toCrack, min, max);
                            boolean found = toCrack.toString().equals(passwordToCrack);
                            if (found)
                            {
                                crackedPassword = toCrack;
                                passwordFound = found;
                                break;
                            }
                        }
                    }
                }));
            }
            else
            {
                break;
            }
        }
    }
    executor.shutdownNow();
}

public String constructString3(long number, long min, long max)
{
    StringBuilder text = new StringBuilder();
    if (number > Long.MAX_VALUE - min)
    {
        number = Long.MAX_VALUE - min;
    }
    ArrayList<Long> vector = new ArrayList<Long>(10);
    vector.add(min - 1 + number);
    long range = max - min + 1;
    boolean nextLetter = false;
    for (int i = 0; i < vector.size(); i++)
    {
        long nextLetterCounter = 0;
        while (vector.get(i) > max)
        {
            nextLetter = true;
            long multiplicator = Math.abs(vector.get(i) / range);
            if ((vector.get(i) - (multiplicator * range)) < min)
            {
                multiplicator -= 1;
            }
            vector.set(i, vector.get(i) - (multiplicator * range));
            nextLetterCounter += multiplicator;
        }
        if (nextLetter)
        {
            vector.add((long) (min + nextLetterCounter - 1));
            nextLetter = false;
        }
        text.append((char) vector.get(i).intValue());
    }
    return text.toString();
}

}

And C# Code:

Entry.cs

using System;

namespace PasswordCracker
{
class Entry
{
    public static void Main(String[] args)
    {
        Console.Out.WriteLine("Type password to be cracked:");
        String input = Console.In.ReadLine();
        PasswordCracker cracker = new PasswordCracker();
        Console.Out.WriteLine("Multithreaded");
        cracker.runMulti(input);            
        cracker = new PasswordCracker();
        Console.Out.WriteLine("Singlethreaded");
        cracker.runSingle(input);            
        Console.Out.WriteLine("Finished...");
        Console.ReadKey();
    }
}
}

PasswordCracker.cs

using System;
using System.Collections.Generic;
using System.Text;

using System.Diagnostics;
using System.Threading.Tasks;
using System.Threading;

namespace PasswordCracker
{

public class PasswordCracker
{

    String passwordToCrack;
    public bool passwordFound;
    int min;
    int max;
    StringBuilder crackedPassword;

    public void prepare(String text)
    {
        passwordToCrack = text;

        passwordFound = false;
        min = 32;
        max = 126;
        crackedPassword = new StringBuilder();
        crackedPassword.Append((char)(min - 1));
    }

    public void result()
    {
        Console.Out.WriteLine("Cracked Password is: " + crackedPassword.ToString());
    }

    public void incrementString(StringBuilder text, int min, int max)
    {
        text[0] = (char)((text[0]) + 1);
        for (int i = 0; i < text.Length; i++)
        {
            if (text[i] > (char)(max))
            {
                text[i] = (char)(min);
                if (text.Length == i + 1)
                {
                    text.Append((char)(min));
                }
                else
                {
                    text[i + 1] = (char)((text[i + 1]) + 1);
                }
            }
        }
    }

    public void runMulti(String text)
    {
        prepare(text);
        Stopwatch time = new Stopwatch();
        time.Start();
        doItMulti();
        Console.Out.WriteLine(time.Elapsed.TotalSeconds);
        result();

    }

    public void runSingle(String text)
    {
        prepare(text);
        Stopwatch time = new Stopwatch();
        time.Start();
        doItSingle();
        Console.Out.WriteLine(time.Elapsed.TotalSeconds);
        result();
    }

    public void doItSingle()
    {
        while (passwordFound == false)
        {
            incrementString(crackedPassword, min, max);
            passwordFound = crackedPassword.ToString().Equals(passwordToCrack);
        }
    }

    public void doItMulti()
    {
        int cores = Environment.ProcessorCount;
        long step = 2000;
        List<Task> tasks = new List<Task>(cores);
        for (long i = 0; i < long.MaxValue; i += step)
        {
            while (tasks.Count > cores)
            {
                for (int a = 0; a < tasks.Count;a++)
                {
                    if (tasks[a].IsCompleted)
                    {
                        tasks.RemoveAt(a);
                        break;
                    }
                }
                Thread.Sleep(0);
            }
            {
                long j = i;
                if (passwordFound == false)
                {
                    tasks.Add(Task.Factory.StartNew(delegate
                    {
                        long border = j + step;
                        StringBuilder toCrack = new StringBuilder(10);
                        toCrack.Append(constructString3(j, min, max));
                        for (long k = j; k < border; k++)
                        {
                            incrementString(toCrack, min, max);
                            bool found = toCrack.ToString().Equals(passwordToCrack);
                            if (found)
                            {
                                crackedPassword = toCrack;
                                passwordFound = found;
                                break;
                            }
                        }
                    }));
                }
                else
                {
                    break;
                }
            }
        }
    }

    public String constructString3(long number, long min, long max)
    {
        StringBuilder text = new StringBuilder();
        if (number > long.MaxValue - min)
        {
            number = long.MaxValue - min;
        }
        List<long> vector = new List<long>(10);
        vector.Add(min - 1 + number);
        long range = max - min + 1;
        bool nextLetter = false;
        for (int i = 0; i < vector.Count; i++)
        {
            long nextLetterCounter = 0;
            while (vector[i] > max)
            {
                nextLetter = true;
                long multiplicator = Math.Abs(vector[i] / range);
                if ((vector[i] - (multiplicator * range)) < min)
                {
                    multiplicator -= 1;
                }
                vector[i] = vector[i] - (multiplicator * range);
                nextLetterCounter += multiplicator;
            }
            if (nextLetter)
            {
                vector.Add((min + nextLetterCounter - 1));
                nextLetter = false;
            }
            text.Append((char)(vector[i]));
        }
        return text.ToString();
    }
}
}
Nogiax
  • 35
  • 2
  • 8
  • 1
    Did you run the C# version in release mode, or debug mode? – Daniel Mann Feb 19 '12 at 18:37
  • possible duplicate of [Java vs C#: Are there any studies that compare their execution speed?](http://stackoverflow.com/questions/1049004/java-vs-c-are-there-any-studies-that-compare-their-execution-speed) – David Anderson Feb 19 '12 at 18:41
  • 1
    "Thread.Sleep(0);" What are you doing? Your threading code seems to be very strange. – usr Feb 19 '12 at 19:02
  • Project is set to release mode. Thread.sleep(0) was a somewhat lazy way to not let the main thread take ressources ( wait and notify would be the next thing to do if this was a real project ). – Nogiax Feb 19 '12 at 19:10
  • 1
    A benchmark like this is essentially equivalent to having a Sleep(random). You can't benchmark in a general random direction, sprinkling Thread.sleep(0)'s around the code. – MK. Feb 19 '12 at 22:16

3 Answers3

1

I generally avoid helping with questions like these because the context is intrinsically malicious, and I don't know you or your intentions with code as such. However, I will tell you that your test is already flawed because you are testing them consecutively which has a low chance of the applications getting similar CPU cycles and such. I suggest using actual profiling tools that are made to actually test for such data, such as Visual Studio Performance Tools, or some other profiling suite, which generally also include tools to compare the results.

Voted to close, possible duplicate: Java vs C#: Are there any studies that compare their execution speed?

Community
  • 1
  • 1
David Anderson
  • 13,558
  • 5
  • 50
  • 76
  • 1
    i can ensure you that this is just for learning purposes to get back into both languages. I actually stripped the java code and left out the javascript test etc. to really compare the same algorithm in java and C#. no *evil* thoughts behind it. – Nogiax Feb 19 '12 at 19:06
1

Try adding the -o2 compiler flag. That should increase c#'s performance.

Tim Post
  • 33,371
  • 15
  • 110
  • 174
pyCthon
  • 11,746
  • 20
  • 73
  • 135
  • Forgot to mention that im using VS 2010 Pro. In the projectsettings i already clicked on "Optimize". Is that the same? if not could you tell me where i have to put the -o2? – Nogiax Feb 19 '12 at 19:08
  • never use visual studios so i'm sorry i can't help you , but i'm sure you can find something with a quick google – pyCthon Feb 22 '12 at 02:35
1

The short answer is, the smaller your tasks the greater the relative overhead. The overhead is largely fixed and can be 10 - 1000x higher than the task you are trying to perform. In each task try to perform 10 - 10,000x as much work making the overhead relatively small. Ideally, just divide all your work into N tasks (where N is the number of cores you have e.g. Runtime.getRuntime().availableProcessors() ) That way all your cores will be busy and you won't have to worry about the queue size (it will be 0 ;)


Longer answer....

I would make sure you are using the -server in all cases as this can improve the benchmark (or atleast give you more consistent results)

When you make a process multi-threaded you add overhead (locking/synchronization/cache coherency) and in return your tasks can run concurrently. It is very easy to write a program which has far more overhead than it gains by concurrency.

Here is an article I wrote on some of the pitfalls of writing a multi-thread program from a simple example and how a multi-threaded program can be much, much slower (trillions of times slower) http://vanillajava.blogspot.com/2011/11/why-concurency-examples-are-confusing.html

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Thank your for your explanation but i already know that. My code shows this too when you try to *crack* a password with less than 4 letters ( with my configuration 1 ). It works perfectly in Java but not in C#. So what is the *problem* with C#? – Nogiax Feb 19 '12 at 22:12
  • As a Java person, I could easily blame C# ;) But I would suggest its always best to compare optimised solutions. Comparing ineffecient solutions may not be fair. e.g. One thing Java is good at is removing pointless code and it tends to be better at it than even C, its just real programs its slower at. ;) – Peter Lawrey Feb 19 '12 at 22:16
  • Ok, this comment looks like an answer i would accept for the question ;) If noone has another idea i will accept this as an answer – Nogiax Feb 19 '12 at 22:36
  • Normally I would suggest you wait for more answers, but since its been closed... As its "not a real question" I guess there must be three not real answers. ;) – Peter Lawrey Feb 19 '12 at 22:42
  • 1
    Its closed because there are too many duplicate and related questions about Java and C# performance (directly related to brute force or not), he should be using real profiling tools because the question only poses speculative answers and information since we aren't on the same hardware configuration for testing and providing his results. Its just not a real question at this point that will be helpful to other people visiting SO in the future. If he had provided some existing results that showed that his testing proved real-world results that are different in the languages it would be different – David Anderson Feb 21 '12 at 02:04
  • 1
    He can only get the real answer he is looking for by measuring and finding what parts of the code are slower, and optimizing from there. Mirroring the source code doesn't always produce the same results, his question is sort of backwards to what it should be. – David Anderson Feb 21 '12 at 02:07
  • @DavidAnderson I agree with both your points. It is useful for people to know what are the pitfalls of comparing two systems and what to avoid. You are right that he doesn't have the setup for a fair comparison and perhaps he now understands how that might be achieved. – Peter Lawrey Feb 21 '12 at 08:29
  • I agree, and hopefully our answers pointed him in the correct direction. – David Anderson Feb 21 '12 at 08:41
  • So I still think providing assistance is useful even if IMHO he is asking the wrong question. – Peter Lawrey Feb 21 '12 at 12:06
  • increment string is not the way to do this as your performance is entirely bound by stringbuilders performance/design. String builder in C# is not usually used with indexers.. just add build the string and retrieve. If i did this in C# i would probably work with a mutable char[] not string builder. – user1496062 Dec 03 '13 at 02:52