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

Почему System.IO.Path.Combine имеет 4 перегрузки?

В .NET 4 System.IO.Path имеет следующие перегрузки для метода Combine:

public static string Combine(params string[] paths)
public static string Combine(string path1, string path2)
public static string Combine(string path1, string path2, string path3)
public static string Combine(string path1, string path2, string path3, string path4)

Первый был добавлен в .NET 4 для поддержки любого количества аргументов пути. Второй был уже в более ранних версиях, поэтому я полагаю, что он поддерживается для обратной совместимости.

Но мне любопытно, что такое использование других перегрузок. Разве эти случаи использования уже не охватываются первой сигнатурой метода с помощью params?

edit: Теперь я считаю, что ответ "потому что не все языки имеют поддержку params (а передача массива без поддержки параметров неудобна)". Тем не менее, разум улья в стеке, похоже, сильно не согласен. Поэтому, как компромисс, я не принимаю никакого ответа.

4b9b3361

Ответ 1

Я бы заподозрил в производительности, поскольку вам нужно создать промежуточный массив с параметрами, а также накладные расходы на перемещение массива и т.д. Вероятно, есть некоторые внутренние и т.д. случаи, когда есть хороший пример использования фиксированного нумерованного параметра версии.

Обновление

Теперь я проверил тесты производительности, чтобы проверить мои предположения. Это то, что я должен был сделать в первую очередь - я нарушил свою собственную мантру исполнения:

Не думайте - измеряйте.

Мои предположения не совсем правильны, но не совсем ошибочны. Версия с фиксированным параметром 4 немного медленнее, чем версия с 4 параметрами, но 3 и 2 фиксированные изменения выполняют значительно лучше.

Существует ряд проблем с жёсткой тестирования производительности для текущего принятого ответа, в котором говорится, что производительность полностью зависит от версии params - это неверно:

  • Он использует DateTime.Now для синхронизации - всегда используйте секундомер для микро-бенчмаркинга как DatTime.Now только точный между ~ 10ms- > 15ms. Есть бесконечные статьи об этом.
  • Тест распространяется только на версию с 4 параметрами - как насчет версий параметров 3 и 2?
  • Производство и сбор мусора не принимаются во внимание. Один метод может быть быстрее по прямой линии между A- > B, но он может также генерировать много мусора, которые нужно будет очистить на определенном этапе. Это отсроченное снижение производительности, но оно по-прежнему влияет на производительность, поэтому его следует принимать во внимание.
  • Убедитесь, что аргументы имеют реалистичные значения, - реалистично ли объединяются пути одного символа?

У меня есть следующие результаты производительности, в которые я включил варианты аргументов 2, 3 и 4, где можно увидеть, что производительность значительно лучше для 2 и 3 вариаций и незначительно хуже для 4-х вариаций. Варианты аргументов с фиксированным номером в целом быстрее, хотя 3 из них являются наиболее значимыми в терминах этого вопроса (изменение аргумента 2 существовало с .Net 1.1).

***2 Args***
params2:3018.44ms
params2:3007.61ms
params2:2988.52ms
params2:2992.33ms
params2:2995.89ms
args2  :1724.83ms
args2  :1723.97ms
args2  :1727.76ms
args2  :1720.42ms
args2  :1718.24ms
***3 Args***
params3:4168.37ms
params3:4169.61ms
params3:4165.63ms
params3:4161.51ms
params3:4153.61ms
args3  :3476.96ms
args3  :3483.40ms
args3  :3482.49ms
args3  :3595.15ms
args3  :3561.11ms
***4 Args***
params4:4992.71ms
params4:4985.51ms
params4:4995.63ms
params4:5002.47ms
params4:4993.99ms
args4  :4993.02ms
args4  :4992.93ms
args4  :4991.07ms
args4  :4993.04ms
args4  :4995.14ms

Тест:

public void MeasurePathPerformance()
{
    const int TestIterations = 5;
    const string Root = "C:\\xxxxxxxxxx";
    string seg = new string('x', 10);
    string path = null;

    Action<string, Func<double>> test = (name, action) =>
    {
        for (int i = 0; i < TestIterations; i++)
        {
            Console.WriteLine("{0}:{1:F2}ms", name, action());
        }
    };

    Console.WriteLine("***2 Args***");
    Action p2 = () => path = Path.Combine(new[] { Root, seg });
    test("params2", () => TimeTest(p2));
    Action a2 = () => path = Path.Combine(Root, seg);
    test("args2  ", () => TimeTest(a2));

    Console.WriteLine("***3 Args***");
    Action p3 = () => path = Path.Combine(new[] { Root, seg, seg });
    test("params3", () => TimeTest(p3));
    Action a3 = () => path = Path.Combine(Root, seg, seg);
    test("args3  ", () => TimeTest(a3));

    Console.WriteLine("***4 Args***");
    Action p4 = () => path = Path.Combine(new[] { Root, seg, seg, seg });
    test("params4", () => TimeTest(p4));
    Action a4 = () => path = Path.Combine(Root, seg, seg, seg);
    test("args4  ", () => TimeTest(a4));

    Console.WriteLine(path);
}

[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.GC.Collect")]
private static double TimeTest(Action action)
{
    const int Iterations = 10 * 1000 * 1000;

    Action gc = () =>
    {
        GC.Collect();
        GC.WaitForFullGCComplete();
    };

    Action empty = () => { };

    Stopwatch stopwatch1 = Stopwatch.StartNew();

    for (int j = 0; j < Iterations; j++)
    {
        empty();
    }

    double loopElapsed = stopwatch1.Elapsed.TotalMilliseconds;

    gc();

    action(); //JIT
    action(); //Optimize

    Stopwatch stopwatch2 = Stopwatch.StartNew();

    for (int j = 0; j < Iterations; j++)
    {
        action();
    }

    gc();

    double testElapsed = stopwatch2.Elapsed.TotalMilliseconds;

    return (testElapsed - loopElapsed);
}

Ответ 2

Что относительно синтаксического шугара для языков, которые не поддерживают что-то похожее на ключевое слово С# - "params"

Update

Устранена проблема с производительностью, так как я измерил только версию с 4 параметрами, но не версию параметров 3 и 2, которая выполняется быстрее

Производительность не является причиной. (См. Мой бенчмарк ниже)

Benchmark

Я измерил производительность. К моему удивлению, версия массива была немного лучше.

    [TestMethod]
    public void MeasurePathPerfomance()
    {
        // repeat several times to avoid jit-issues
        for (int j = 0; j < 10; j++)
        {
            {
                DateTime start = DateTime.Now;

                string result;
                for (int i = 0; i < 30000; i++)
                {
                    result = System.IO.Path.Combine("a", "b", "c", "d"); // use 4 parameter version
                }
                TimeSpan spend = DateTime.Now - start;
                System.Console.WriteLine("4 param : {0}", spend.TotalMilliseconds);
            }
            {
                DateTime start = DateTime.Now;

                string result;
                for (int i = 0; i < 30000; i++)
                {
                    result = System.IO.Path.Combine(new string[] { "a", "b", "c", "d" });
                }
                TimeSpan spend = DateTime.Now - start;
                System.Console.WriteLine("array[4] param : {0}", spend.TotalMilliseconds);
            }
        }
    }

результат

    4 param : 10.001
    array[4] param : 9.0009
    4 param : 12.0012
    array[4] param : 8.0008
    4 param : 12.0012
    array[4] param : 10.001
    4 param : 11.0011
    array[4] param : 9.0009
    4 param : 11.0011
    array[4] param : 11.0011
    4 param : 11.0011
    array[4] param : 9.0009
    4 param : 10.001
    array[4] param : 8.0008
    4 param : 10.001
    array[4] param : 9.0009
    4 param : 11.0011
    array[4] param : 9.0009
    4 param : 11.0011
    array[4] param : 9.0009

Забастовкa >

Ответ 3

Одной из возможных причин может быть также снижение давления на сборщик мусора. Перегрузка params-array создает новый массив каждый раз при вызове метода. Если метод вызывается часто, создается множество объектов временного массива, увеличивая давление на сборщик мусора.

Ответ 4

Обратная совместимость - лучшая причина, о которой я могу думать. Вероятно, перегрузки вызвали первый метод.

Ответ 5

Только из-за производительности производительность последних 3 больше, чем первый метод.

Если вы хотите знать реализацию, просто посмотрите на mscorlib с отражателем, вы увидите, что производительность будет лучше в последних трех функциях

Ответ 6

Я думаю, причина в том, что большинство программистов используют одно, два, три или четыре значения для объединения и больше, чем вы хотите, чтобы лучше было реализовать массив, чтобы использовать больше значений.

Пример

Sum(a , b); //fine
Sum(a , b , c);//fine
Sum(a , b , c , d);//fine
Sum(a , b , c , d ,....); //now I think you think everyone think  even Microsoft also thinks, its better to implement array here

// something like this
Sum(params var[] n);   

Итак, вы обнаружите, что большинство методов содержит 1,2,3,4 аргумента, а затем params