Подтвердить что ты не робот

Почему сравнение строк (CompareTo) быстрее в Java, чем в С#?

EDIT3:

Используя

StringComparer comparer1 = StringComparer.Ordinal;

вместо

IComparable v
IComparable w
comparer1.Compare(v, w)

Решение проблемы времени выполнения.


Я сделал несколько тестов для алгоритмов сортировки (например, Quicksort, Mergesort) в Java и С#.

Я использовал Java 7 и .NET Framework 4.5 для реализации и выполнения моих алгоритмов. Он показал, что все алгоритмы могут обеспечить более эффективное время автономной работы с использованием Java.

Некоторые примеры времени выполнения для Quicksort:

С#

  • n = 1000000 4433 мс
  • n = 2000000 10047 мс

Java

  • n = 1000000 1311 мс
  • n = 2000000 3164 мс

Затем я сделал измерения с помощью инструментов профилирования: С# использовал 75% времени выполнения для сравнения строк (например, CompareTo), тогда как Java использовала только 2% времени выполнения для сравнения.

Почему сравнения строк настолько дороги в С#, а не на Java?

EDIT: Я также тестировал функцию сортировки С# Arrays.sort(INPUT), которая могла бы достичь около 3000 мс для n = 1000000, поэтому я не думаю, что этот код является проблемой. Но в любом случае:

Здесь код для Quicksort:

public class Quicksort {

public static void sort(IComparable[] a) {
    sort(a, 0, a.Length - 1);
}

private static void sort(IComparable[] a, int lo, int hi) { 
    if (hi <= lo) return;
    int j = partition(a, lo, hi);
    sort(a, lo, j-1);
    sort(a, j+1, hi);
}

private static int partition(IComparable[] a, int lo, int hi) {
    int i = lo;
    int j = hi + 1;
    IComparable v = a[lo];
    while (true) { 

        while (less(a[++i], v))
            if (i == hi) break;

        while (less(v, a[--j]))
            if (j == lo) break; 


        if (i >= j) break;

        exch(a, i, j);
    }

    exch(a, lo, j);

    return j;
}


public static IComparable select(IComparable[] a, int k) {
    if (k < 0 || k >= a.Length) {
        throw new Exception("Selected element out of bounds");
    }
    Rnd.Shuffle(a);
    int lo = 0, hi = a.Length - 1;
    while (hi > lo) {
        int i = partition(a, lo, hi);
        if      (i > k) hi = i - 1;
        else if (i < k) lo = i + 1;
        else return a[i];
    }
    return a[lo];
}


private static bool less(IComparable v, IComparable w) {
    return (v.CompareTo(w) < 0);
}

private static void exch(Object[] a, int i, int j) {
    Object swap = a[i];
    a[i] = a[j];
    a[j] = swap;
}

}

Быстрое сортирование измеряется следующим образом:

Stopwatch.Restart();
Quicksort.sort(stringArray);
Stopwatch.Stop();

EDIT2: Кто-то хотел увидеть версию Java. Это точно так же, я просто использую Comparable вместо IComparable и Array.length вместо Array.Length

4b9b3361

Ответ 1

поэтому я не думаю, что этот код является проблемой

Я прошу разницы. В обоих случаях вы не сравниваете одно и то же. По общему признанию, это не помогает, что вы также не показали нам, как вы генерируете строки и т.д., Но Java и .NET выполняют различные сравнения строк по умолчанию.

Из Java String.compareTo:

Сравнивает две строки лексикографически.

И из .NET String.compareTo:

Этот метод выполняет сравнение слов (чувствительность к регистру и чувствительность к культуре) с использованием текущей культуры.

Неудивительно, что это медленнее, чем чисто лексикографическое сравнение.

Легко это увидеть, просто сравнивая .NET с собой...

using System;
using System.Diagnostics;
using System.Text;

class Test
{
    static void Main()
    {
        string[] source = GenerateRandomStrings();
        string[] workingSpace = new string[source.Length];

        CopyAndSort(source, workingSpace);
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < 1000; i++)
        {
            CopyAndSort(source, workingSpace);
        }
        sw.Stop();
        Console.WriteLine("Elapsed time: {0}ms", 
                          (long) sw.Elapsed.TotalMilliseconds);
    }

    static string[] GenerateRandomStrings()
    {
        Random rng = new Random();
        string[] ret = new string[10000];
        for (int i = 0; i < ret.Length; i++)
        {
            ret[i] = GenerateRandomString(rng);
        }
        return ret;
    }

    static string GenerateRandomString(Random rng)
    {
        char[] chars = new char[30];
        for (int i = 0; i < chars.Length; i++)
        {
            chars[i] = (char) rng.Next('A', 'z' + 1);
        }
        return new string(chars);
    }

    static void CopyAndSort(string[] original, string[] workingSpace)
    {
        Array.Copy(original, 0, workingSpace, 0, original.Length);
        Array.Sort(workingSpace);
        // Array.Sort(workingSpace, StringComparer.Ordinal);
    }
}

Попробуйте сами, изменив метод CopyAndSort на основе того, задаете ли вы порядковый сопоставитель строк или нет. Это намного быстрее с порядковым компаратором, по крайней мере, на моей коробке.

Ответ 2

Я не уверен на 100%, но я думаю, что на С# платформа .NET могла бы по умолчанию выполнять некоторые расширенные проверки, такие как правильное пропущение аннотаций или символов Unicode в виде пробелов, а Java, возможно, не делает их по умолчанию. Я думаю, что Java runtime выполняет более простое сравнение байтов с 2 байтами, но я могу быть очень неправ здесь, так как довольно давно я коснулся деталей работы с кодировками на Java, так что это чистое угадывание.

Другое дело: могут быть некоторые различия в сравнении по умолчанию между этими двумя платформами. Если вы просто используете настройки по умолчанию, без каких-либо точных настроек, я бы предположил, что вы просто неявно просили разные варианты поведения.

По крайней мере, в .Net есть много вариантов сравнения строк. Возможно, вы просто взяли аналогичную функцию, которая фактически делает другое сравнение, которое представляет собой Java. Вы пробовали с подробной перегрузкой, например http://msdn.microsoft.com/en-us/library/cc190416.aspx? Обратите внимание, что это метод static, который нужно использовать бит по-разному:

var result1 = "mom".CompareTo("dad");
var result2 = string.Compare("mom", "dad", ...);

Пожалуйста, изучите параметры ComparisonOptions и/или CultureInfo и настройте их так, чтобы общее поведение соответствовало поведению Java как можно ближе. Кроме того, вам может потребоваться выбрать другую перегрузку на стороне Java, если их больше.

Кроме того, другое отличие может быть несколько сложным фактом: метод CompareTo, который вы тестируете, представляет собой реализацию интерфейса IComparable<T>, который предназначен для перекрестного сравнения различных объектов, реализующих этот интерфейс, и статического метода Compare предназначен для сравнения только строк. Они просто могут быть оптимизированы для разных вещей. Если между ними будет какая-либо разница в производительности, я бы поставил на то, что статический Compare быстрее для сравнения строк и vs-строк.