Я предпочитаю добавить тестирование по временной петле на наличие элемента, поскольку это позволяет вам выйти раньше, если найден элемент, и, следовательно, потенциально более эффективно, чем жестко закодированное время ожидания, которое всегда будет завершено.
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
Короткий ответ: это зависит.
ответ Long: , если у Вас уже есть массив строк для конкатенации вместе (с разделителем), String.Join
, самый быстрый способ сделать его.
String.Join
может просмотреть все строки для разработки точной длины, в которой это нуждается, затем пойдите снова и скопируйте все данные. Это означает, что будет никакой дополнительное включенное копирование. только [1 111] оборотная сторона - то, что это должно перейти строки дважды, что означает потенциально уносить кэш памяти больше раз, чем необходимый.
, Если Вы не делаете , имеют строки как массив заранее, это , вероятно быстрее для использования StringBuilder
- но будут ситуации, где это не. При использовании StringBuilder
партии выполнения средств и много копий, затем создание массива и затем вызов String.Join
могут быть быстрее.
РЕДАКТИРОВАНИЕ: Это с точки зрения единственного вызова к String.Join
по сравнению с набором вызовов к StringBuilder.Append
. В исходном вопросе у нас было два разных уровня String.Join
вызовы, таким образом, каждый из вложенных вызовов создаст промежуточную строку. Другими словами, это еще более сложно и более твердо предположить о. Я был бы удивлен значительно видеть так или иначе "победу" (в условиях сложности) с типичными данными.
РЕДАКТИРОВАНИЕ: Когда я буду дома, я опишу сравнительный тест, который является столь же болезненным как возможно для StringBuilder
. В основном, если у Вас есть массив, где каждый элемент о дважды размере предыдущего, и Вы получаете его просто право, необходимо быть в состоянии вызвать копию для каждого добавлять (элементов, не разделителя, хотя это должно быть принято во внимание также). В той точке это почти настолько же плохо, как конкатенация простой строки - но String.Join
не будет иметь никаких проблем.
Вот моя тестовая буровая установка, с помощью 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();
}
}
}
Я не думаю так. Просматривая Отражатель, реализацию 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));
}
}
Если 1%-е различие не превращается во что-то значительное с точки зрения времени, вся программа берет для выполнения, это похоже на микрооптимизацию. Я написал бы код, это является самым читаемым/понятным и не беспокойство о 1%-м различии в производительности.
Atwood связали сообщение отчасти с этим приблизительно месяц назад:
да. Если Вы сделаете больше чем несколько соединений, то это будет много быстрее.
, Когда Вы делаете string.join, время выполнения имеет к:
, Если Вы делаете два соединения, это должно скопировать данные дважды и так далее.
StringBuilder выделяет один буфер с пространством для экономии, таким образом, данные могут быть добавлены, не имея необходимость копировать исходную строку. Как существует пространство, перенесенное в буфере, добавленная строка может быть записана в буфер непосредственно. Тогда это просто должно скопировать всю строку однажды в конце.