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

Оптимизация агрегирования для конкатенации строк

Обновить - для тех, кто имеет преувеличенное настроение, вы можете предположить, что Aggregate все равно производит нормальный результат, независимо от того, какая функция передана ему, в том числе в случае оптимизации.

Я написал эту программу для создания длинной строки целых чисел от 0 до 19999 по запятой.

using System;
using System.Linq;
using System.Diagnostics;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            const int size = 20000;

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();
            Enumerable.Range(0, size).Select(n => n.ToString()).Aggregate((a, b) => a + ", " + b);
            stopwatch.Stop();

            Console.WriteLine(stopwatch.ElapsedMilliseconds + "ms");
        }
    }
}

Когда я запустил его, он говорит:

5116ms

Более пяти секунд, ужасно. Конечно, это потому, что вся строка копируется каждый раз вокруг цикла.

Но что, если сделать одно очень небольшое изменение, указанное комментарием?

using System;
using System.Linq;
using System.Diagnostics;

namespace ConsoleApplication5
{
    using MakeAggregateGoFaster;  // <---- inserted this

    class Program
    {
        static void Main(string[] args)
        {
            const int size = 20000;

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();
            Enumerable.Range(0, size).Select(n => n.ToString()).Aggregate((a, b) => a + ", " + b);
            stopwatch.Stop();

            Console.WriteLine(stopwatch.ElapsedMilliseconds + "ms");
        }
    }
}

Теперь, когда я запустил его, он говорит:

42ms

Более 100 раз быстрее.

Вопрос

Что в пространстве имен MakeAggregateGoFaster?

Обновление 2: Написал мой ответ здесь.

4b9b3361

Ответ 1

Вы "переопределяете" System.Linq.Aggregate своим собственным методом расширения в пространстве имен MakeAggregateGoFaster.

Возможно, специализируется на IEnumerable<string> и использует StringBuilder?

Может быть, вместо Func<string, string, string> взять Expression<Func<string, string, string>>, чтобы он мог анализировать дерево выражений и компилировать код, который использует StringBuilder вместо прямого вызова функции?

Просто гадать.

Ответ 2

Почему бы не использовать одну из других форм агрегата?

Enumerable.Range(0, size ).Aggregate(new StringBuilder(),
        (a, b) => a.Append(", " + b.ToString()),
        (a) => a.Remove(0,2).ToString());

Вы можете указать любой тип для вашего семени, выполнить любое форматирование или пользовательские вызовы в первой функции лямбда, а затем настроить тип вывода во второй лямбда-функции. Встроенные функции уже обеспечивают необходимую гибкость. Мои трассы шли от 1444 мс до 6 мс.

Ответ 3

Не отвечая на вопрос, но я думаю, что стандартные шаблоны здесь должны использовать StringBuilder или string.Join:

string.join(", ",Enumerable.Range(0, size).Select(n => n.ToString()).ToArray())

Ответ 4

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

Решение 1 (выполняется мгновенно, проблема не проверяется):

public static string Aggregate(this IEnumerable<string> l, Func<string, string, string> f) {
     return "";
}

Решение 2 (работает так же быстро, как требуется, но игнорирует делегат полностью):

public static string Aggregate(this IEnumerable<string> l, Func<string, string, string> f) {
    StringBuilder sb = new StringBuilder();
    foreach (string item in l)
        sb.Append(", ").Append(item);
    return sb.Remove(0,2).ToString();
}

Ответ 5

Ну, это будет целиком зависеть от того, какой код находится в пространстве имен MageAggregateGoFaster, не так ли?

Это пространство имен не является частью среды выполнения .NET, поэтому вы связались с каким-то специальным кодом.

Лично я бы подумал, что что-то, что распознает конкатенацию строк или подобное, и создает список или аналогичный, затем выделяет один большой StringBuilder и использует Append.

Грязное решение будет:

namespace MakeAggregateGoFaster
{
    public static class Extensions
    {
        public static String Aggregate(this IEnumerable<String> source, Func<String, String, String> fn)
        {
            StringBuilder sb = new StringBuilder();
            foreach (String s in source)
            {
                if (sb.Length > 0)
                    sb.Append(", ");
                sb.Append(s);
            }

            return sb.ToString();
        }
    }
}

грязный, потому что этот код, делая то, что вы говорите, испытываете с вашей программой, вообще не использует делегат функции. Однако это сократит время выполнения от 2800 мс до 11 мс на моем компьютере и все равно даст те же результаты.

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