Строка. Соединение по сравнению с StringBuilder: который быстрее?

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

Dim t As Date, ele As Object
Const MAX_WAIT_SEC As Long = 10 '<==Adjust wait time

While ie.Busy Or ie.readyState < 4: DoEvents: Wend
t = timer
Do 
    DoEvents
    On Error Resume Next
    Set ele = IE.document.getElementByID("firstname")
    If Timer - t > MAX_WAIT_SEC Then Exit Do
    On Error GoTo 0
Loop While ele Is Nothing

If Not ele Is Nothing Then
    'do something 
End If
73
задан Hosam Aly 26 June 2019 в 12:14
поделиться

6 ответов

Короткий ответ: это зависит.

ответ Long: , если у Вас уже есть массив строк для конкатенации вместе (с разделителем), String.Join, самый быстрый способ сделать его.

String.Join может просмотреть все строки для разработки точной длины, в которой это нуждается, затем пойдите снова и скопируйте все данные. Это означает, что будет никакой дополнительное включенное копирование. только [1 111] оборотная сторона - то, что это должно перейти строки дважды, что означает потенциально уносить кэш памяти больше раз, чем необходимый.

, Если Вы не делаете , имеют строки как массив заранее, это , вероятно быстрее для использования StringBuilder - но будут ситуации, где это не. При использовании StringBuilder партии выполнения средств и много копий, затем создание массива и затем вызов String.Join могут быть быстрее.

РЕДАКТИРОВАНИЕ: Это с точки зрения единственного вызова к String.Join по сравнению с набором вызовов к StringBuilder.Append. В исходном вопросе у нас было два разных уровня String.Join вызовы, таким образом, каждый из вложенных вызовов создаст промежуточную строку. Другими словами, это еще более сложно и более твердо предположить о. Я был бы удивлен значительно видеть так или иначе "победу" (в условиях сложности) с типичными данными.

РЕДАКТИРОВАНИЕ: Когда я буду дома, я опишу сравнительный тест, который является столь же болезненным как возможно для StringBuilder. В основном, если у Вас есть массив, где каждый элемент о дважды размере предыдущего, и Вы получаете его просто право, необходимо быть в состоянии вызвать копию для каждого добавлять (элементов, не разделителя, хотя это должно быть принято во внимание также). В той точке это почти настолько же плохо, как конкатенация простой строки - но String.Join не будет иметь никаких проблем.

106
ответ дан Jon Skeet 24 November 2019 в 12:16
поделиться

Вот моя тестовая буровая установка, с помощью int[][] для простоты; результаты сначала:

Join: 9420ms (chk: 210710000
OneBuilder: 9021ms (chk: 210710000

(обновляют для double результаты:)

Join: 11635ms (chk: 210710000
OneBuilder: 11385ms (chk: 210710000

(ре обновления 2048 * 64 * 150)

Join: 11620ms (chk: 206409600
OneBuilder: 11132ms (chk: 206409600

и с OptimizeForTesting включил:

Join: 11180ms (chk: 206409600
OneBuilder: 10784ms (chk: 206409600

Поэтому быстрее, но не в широком масштабе так; буровая установка (выполненный в консоли, в режиме выпуска, и т.д.):

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

namespace ConsoleApplication2
{
    class Program
    {
        static void Collect()
        {
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
        }
        static void Main(string[] args)
        {
            const int ROWS = 500, COLS = 20, LOOPS = 2000;
            int[][] data = new int[ROWS][];
            Random rand = new Random(123456);
            for (int row = 0; row < ROWS; row++)
            {
                int[] cells = new int[COLS];
                for (int col = 0; col < COLS; col++)
                {
                    cells[col] = rand.Next();
                }
                data[row] = cells;
            }
            Collect();
            int chksum = 0;
            Stopwatch watch = Stopwatch.StartNew();
            for (int i = 0; i < LOOPS; i++)
            {
                chksum += Join(data).Length;
            }
            watch.Stop();
            Console.WriteLine("Join: {0}ms (chk: {1}", watch.ElapsedMilliseconds, chksum);

            Collect();
            chksum = 0;
            watch = Stopwatch.StartNew();
            for (int i = 0; i < LOOPS; i++)
            {
                chksum += OneBuilder(data).Length;
            }
            watch.Stop();
            Console.WriteLine("OneBuilder: {0}ms (chk: {1}", watch.ElapsedMilliseconds, chksum);

            Console.WriteLine("done");
            Console.ReadLine();
        }
        public static string Join(int[][] array)
        {
            return String.Join(Environment.NewLine,
                    Array.ConvertAll(array,
                      row => String.Join(",",
                        Array.ConvertAll(row, x => x.ToString()))));
        }
        public static string OneBuilder(IEnumerable<int[]> source)
        {
            StringBuilder sb = new StringBuilder();
            bool firstRow = true;
            foreach (var row in source)
            {
                if (firstRow)
                {
                    firstRow = false;
                }
                else
                {
                    sb.AppendLine();
                }
                if (row.Length > 0)
                {
                    sb.Append(row[0]);
                    for (int i = 1; i < row.Length; i++)
                    {
                        sb.Append(',').Append(row[i]);
                    }
                }
            }
            return sb.ToString();
        }
    }
}
30
ответ дан Marc Gravell 24 November 2019 в 12:16
поделиться

Я не думаю так. Просматривая Отражатель, реализацию String.Join взгляды, очень оптимизированные. Это также обладает дополнительным преимуществом знания, что общий размер строки создается заранее, таким образом, этому не нужно никакое перераспределение.

я создал два метода тестирования сравнить их:

public static string TestStringJoin(double[][] array)
{
    return String.Join(Environment.NewLine,
        Array.ConvertAll(array,
            row => String.Join(",",
                       Array.ConvertAll(row, x => x.ToString()))));
}

public static string TestStringBuilder(double[][] source)
{
    // based on Marc Gravell's code

    StringBuilder sb = new StringBuilder();
    foreach (var row in source)
    {
        if (row.Length > 0)
        {
            sb.Append(row[0]);
            for (int i = 1; i < row.Length; i++)
            {
                sb.Append(',').Append(row[i]);
            }
        }
    }
    return sb.ToString();
}

я выполнил каждый метод 50 раз, передающий в массиве размера [2048][64]. Я сделал это для двух массивов; один заполненный с нулями и другим заполненным случайными значениями. Я получил следующие результаты на своей машине (P4 3,0 ГГц, одножильные, никакой HT, выполнив режим Release от CMD):

// with zeros:
TestStringJoin    took 00:00:02.2755280
TestStringBuilder took 00:00:02.3536041

// with random values:
TestStringJoin    took 00:00:05.6412147
TestStringBuilder took 00:00:05.8394650

Увеличение размера массива к [2048][512], при сокращении числа повторений к 10 получило меня следующие результаты:

// with zeros:
TestStringJoin    took 00:00:03.7146628
TestStringBuilder took 00:00:03.8886978

// with random values:
TestStringJoin    took 00:00:09.4991765
TestStringBuilder took 00:00:09.3033365

результаты повторяемы (почти; с маленькими колебаниями, вызванными различными случайными значениями). По-видимому String.Join немного быстрее большую часть времени (хотя очень маленьким полем).

Это - код, который я использовал для тестирования:

const int Iterations = 50;
const int Rows = 2048;
const int Cols = 64; // 512

static void Main()
{
    OptimizeForTesting(); // set process priority to RealTime

    // test 1: zeros
    double[][] array = new double[Rows][];
    for (int i = 0; i < array.Length; ++i)
        array[i] = new double[Cols];

    CompareMethods(array);

    // test 2: random values
    Random random = new Random();
    double[] template = new double[Cols];
    for (int i = 0; i < template.Length; ++i)
        template[i] = random.NextDouble();

    for (int i = 0; i < array.Length; ++i)
        array[i] = template;

    CompareMethods(array);
}

static void CompareMethods(double[][] array)
{
    Stopwatch stopwatch = Stopwatch.StartNew();
    for (int i = 0; i < Iterations; ++i)
        TestStringJoin(array);
    stopwatch.Stop();
    Console.WriteLine("TestStringJoin    took " + stopwatch.Elapsed);

    stopwatch.Reset(); stopwatch.Start();
    for (int i = 0; i < Iterations; ++i)
        TestStringBuilder(array);
    stopwatch.Stop();
    Console.WriteLine("TestStringBuilder took " + stopwatch.Elapsed);

}

static void OptimizeForTesting()
{
    Thread.CurrentThread.Priority = ThreadPriority.Highest;
    Process currentProcess = Process.GetCurrentProcess();
    currentProcess.PriorityClass = ProcessPriorityClass.RealTime;
    if (Environment.ProcessorCount > 1) {
        // use last core only
        currentProcess.ProcessorAffinity
            = new IntPtr(1 << (Environment.ProcessorCount - 1));
    }
}
18
ответ дан MSeifert 24 November 2019 в 12:16
поделиться

Если 1%-е различие не превращается во что-то значительное с точки зрения времени, вся программа берет для выполнения, это похоже на микрооптимизацию. Я написал бы код, это является самым читаемым/понятным и не беспокойство о 1%-м различии в производительности.

10
ответ дан tvanfosson 24 November 2019 в 12:16
поделиться

Atwood связали сообщение отчасти с этим приблизительно месяц назад:

http://www.codinghorror.com/blog/archives/001218.html

-1
ответ дан Adam Neal 24 November 2019 в 12:16
поделиться

да. Если Вы сделаете больше чем несколько соединений, то это будет много быстрее.

, Когда Вы делаете string.join, время выполнения имеет к:

  1. Выделяют память для получившей строки
  2. , копируют содержание первой строки к началу выходной строки
  3. , копируют содержание второй строки до конца выходной строки.

, Если Вы делаете два соединения, это должно скопировать данные дважды и так далее.

StringBuilder выделяет один буфер с пространством для экономии, таким образом, данные могут быть добавлены, не имея необходимость копировать исходную строку. Как существует пространство, перенесенное в буфере, добавленная строка может быть записана в буфер непосредственно. Тогда это просто должно скопировать всю строку однажды в конце.

-3
ответ дан robasta 24 November 2019 в 12:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: