Здравствуйте, Геннадий Васильев, Вы писали:
WF>>В типичных случаях он отдаёт наружу ровно ту строку, которую внутри себя редактирует. Естественно, с необходимой осторожностью, для гарантии неизменяемости этой отданной строки.
ГВ>То есть он отдаёт строку, держит на неё ссылку и при последующем, например, Append — дублирует её обратно во внутренний буфер?
Типа того. С учётом того, что внутренний буфер — это и есть эта строка (string на самом деле изменяема, внутри mscorlib).
ГВ>
ГВ>String s = strBuilder.ToString(); // Отдал строку
ГВ>strBuilder.Append("ABC"); // Вернул себе копию того, что было отдано через ToString и продолжил модификацию
ГВ>
Здравствуйте, WFrag, Вы писали:
ГВ>>То есть он отдаёт строку, держит на неё ссылку и при последующем, например, Append — дублирует её обратно во внутренний буфер? WF>Типа того. С учётом того, что внутренний буфер — это и есть эта строка (string на самом деле изменяема, внутри mscorlib).
ГВ>>
ГВ>>String s = strBuilder.ToString(); // Отдал строку
ГВ>>strBuilder.Append("ABC"); // Вернул себе копию того, что было отдано через ToString и продолжил модификацию
ГВ>>
ГВ>>Так?
WF>Что-то вроде того.
Тогда берём такой пример:
const int Count = 1000000;
String str = "Text";
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 10; ++i)
sb.Append("0123456789");
String s;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < Count; i++)
{
s = sb.ToString();
}
stopwatch.Stop();
Console.WriteLine("Elapsed 1 {0}", stopwatch.Elapsed);
stopwatch.Reset();
stopwatch.Start();
for (int i = 0; i < Count; i++)
{
s = sb.ToString();
s = sb.ToString();
s = sb.ToString();
s = sb.ToString();
s = sb.ToString();
}
stopwatch.Stop();
Console.WriteLine("Elapsed 5 {0}", stopwatch.Elapsed);
Если твоё предположение верно, то соотношение времен Elapsed1:Elapsed5 должно быть близко к 1:1. Прокрути тест, посмотри на результаты.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, neFormal, Вы писали:
F>>>Фреймворк F>>>Библиотека C>>И? STL подходит 100% под определение фреймворка из приведенных Вами ссылок.
F>rofl F>
C>>>>Не стоит выставлять на показ свои проблемы с логикой.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Здравствуйте, Serginio1, Вы писали:
ГВ>Как это, не будет копирования строки?
А говоришь код понятный.
public override string ToString()
{
string stringValue = this.m_StringValue;
.....................................
stringValue.ClearPostNullChar();// добиваем пространство за Length + 1 нулямиthis.m_currentThread = IntPtr.Zero; // устанавливаем флаг на то что есть ссылка на строкуreturn stringValue; // возвращаем ссылку на внутреннюю строку
}
public StringBuilder Append(string value)
{
if (value != null)
{
string stringValue = this.m_StringValue;
IntPtr currentThread = Thread.InternalGetCurrentThread();
if (this.m_currentThread != currentThread) // Вот здесь если m_currentThread == IntPtr.Zero
{ // Выделяется новая строка
stringValue = string.GetStringForStringBuilder(stringValue, stringValue.Capacity);
}
int length = stringValue.Length;
int requiredLength = length + value.Length;
if (this.NeedsAllocation(stringValue, requiredLength))
{// если места нехватает выделяется новая строка с новым капасити currentString.Capacity * 2;string newString = this.GetNewString(stringValue, requiredLength);
newString.AppendInPlace(value, length);
this.ReplaceString(currentThread, newString);
}
else
{
stringValue.AppendInPlace(value, length);
this.ReplaceString(currentThread, stringValue); // зписывается вместо старой
}
}
return this;
}
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Тогда берём такой пример:
ГВ>
ГВ>const int Count = 1000000;
ГВ>String str = "Text";
ГВ>StringBuilder sb = new StringBuilder();
ГВ>for(int i = 0; i < 10; ++i)
ГВ>sb.Append("0123456789");
ГВ>String s;
ГВ>Stopwatch stopwatch = new Stopwatch();
ГВ>stopwatch.Start();
ГВ>for (int i = 0; i < Count; i++)
ГВ>{
ГВ> s = sb.ToString();
ГВ>}
ГВ>stopwatch.Stop();
ГВ>Console.WriteLine("Elapsed 1 {0}", stopwatch.Elapsed);
ГВ>stopwatch.Reset();
ГВ>stopwatch.Start();
ГВ>for (int i = 0; i < Count; i++)
ГВ>{
ГВ> s = sb.ToString();
ГВ> s = sb.ToString();
ГВ> s = sb.ToString();
ГВ> s = sb.ToString();
ГВ> s = sb.ToString();
ГВ>}
ГВ>stopwatch.Stop();
ГВ>Console.WriteLine("Elapsed 5 {0}", stopwatch.Elapsed);
ГВ>
ГВ>Если твоё предположение верно, то соотношение времен Elapsed1:Elapsed5 должно быть близко к 1:1. Прокрути тест, посмотри на результаты.
Я не делаю предположений, я смотрю рефлектором. Но там не всё так прозрачно. Судя по коду, есть подозрение, что только первый .ToString не вызовет копирования.
Это-то я всё понял, но за комментарии отдельное спасибо.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
S> stringValue.ClearPostNullChar();// добиваем пространство за Length + 1 нулями
S>
Вот этот момент, кстати, непонятный. Судя по коду оно добивает 1 null, причём за null-терминатором строки (который, видимо, играет роль при взаимодействии с нейтивом), то есть это уже как бы второй unll. Но зачем?
ГВ>>String s = strBuilder.ToString(); // Отдал строку
ГВ>>strBuilder.Append("ABC"); // Вернул себе копию того, что было отдано через ToString и продолжил модификацию
ГВ>>
Здравствуйте, WFrag, Вы писали:
WF>Здравствуйте, Serginio1, Вы писали:
S>>
S>> stringValue.ClearPostNullChar();// добиваем пространство за Length + 1 нулями
S>>
WF>Вот этот момент, кстати, непонятный. Судя по коду оно добивает 1 null, причём за null-терминатором строки (который, видимо, играет роль при взаимодействии с нейтивом), то есть это уже как бы второй unll. Но зачем?
Это ты у них спроси. Она и так нуль терминированная. Какие то у них свои тараканы.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, criosray, Вы писали:
C>Здравствуйте, WFrag, Вы писали:
ГВ>>>
ГВ>>>String s = strBuilder.ToString(); // Отдал строку
ГВ>>>strBuilder.Append("ABC"); // Вернул себе копию того, что было отдано через ToString и продолжил модификацию
ГВ>>>
ГВ>>>Так?
WF>>Что-то вроде того.
C>Вовсе не типа того.
Здравствуйте, WFrag, Вы писали:
WF>Я не делаю предположений, я смотрю рефлектором. Но там не всё так прозрачно. Судя по коду, есть подозрение, что только первый .ToString не вызовет копирования.
А я всё больше таймеры обсчитываю. Вот тебе ещё один тест:
// Viva паранойя: разогрев StringBuilder:for (int k = 0; k < 5; ++k)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 2; ++i)
sb.Append("0123456789");
String s = sb.ToString();
}
// Тестconst int Count = 10000000;
const int ConcatCount = 1;
const String StrConcat = "0123456789";
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int k = 0; k < Count; ++k)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ConcatCount; ++i)
sb.Append(StrConcat);
String s = "A";
}
stopwatch.Stop();
TimeSpan tsCreate = stopwatch.Elapsed;
Console.WriteLine("Elapsed creation {0}", stopwatch.Elapsed);
stopwatch.Reset();
stopwatch.Start();
for (int k = 0; k < Count; ++k)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ConcatCount; ++i)
sb.Append(StrConcat);
String s = sb.ToString();
}
stopwatch.Stop();
TimeSpan tsCreateAssign = stopwatch.Elapsed;
Console.WriteLine("Elapsed create+assign {0}", stopwatch.Elapsed);
stopwatch.Reset();
stopwatch.Start();
for (int k = 0; k < Count; ++k)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ConcatCount; ++i)
sb.Append(StrConcat);
String s = sb.ToString();
s = sb.ToString();
}
stopwatch.Stop();
TimeSpan tsCreateAssign2 = stopwatch.Elapsed;
Console.WriteLine("Elapsed create+assign2 {0}", stopwatch.Elapsed);
Console.WriteLine("EC:ECA ~=~ 1:{0}", tsCreateAssign.TotalMilliseconds / tsCreate.TotalMilliseconds);
Console.WriteLine("ECA:ECA2 ~=~ 1:{0}", tsCreateAssign2.TotalMilliseconds / tsCreate.TotalMilliseconds);
double dCAC = tsCreateAssign.TotalMilliseconds - tsCreate.TotalMilliseconds;
double dCA2CA = tsCreateAssign2.TotalMilliseconds - tsCreateAssign.TotalMilliseconds;
Console.WriteLine("ECA-EC = {0} ms (D1)", dCAC / Count);
Console.WriteLine("ECA2-ECA = {0} ms (D2)", dCA2CA / Count);
Console.WriteLine("D2:D1 = {0}:1", dCA2CA / dCAC);
Здесь сравнивается три длительности: создание строки (1), создание + одно присвоение ToString() (2), создание + два присвоения ToString() (3). Потом сравниваются две разницы: (2)-(1) = D1 и (3)-(2) = D2. Если StringBuilder оптимизирован под отдачу первой строки, то D2:D1 должно быть значительно больше, чем 2:1. В принципе, похоже, что так и есть — D2:D1 для десятисимвольной строки при ConcatCount=1 колеблется где-то в районе 3.5:1, то есть второй вызов sb.ToString() значительно медленнее первого. Но возможно, я чего-то не учитываю, например — активности GC.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
На итоговые выводы это не повлияло, но всё равно ошибка.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Serginio1, Вы писали:
ГВ>>Это-то я всё понял, но за комментарии отдельное спасибо. S>А зачем же я коментарии то пишу? S>Там есть такая запись после отдачи строки.
S>
S>this.m_currentThread = IntPtr.Zero;
S>
Судя по всему, это просто маркер текущего потока, который модифицирует строку. Кстати, я не сообразил, что ты привёл код FW-шного стрингбилдера.
public override string ToString()
{
/*
this.m_currentThread - маркер "нахожусь в процессе модификации".
*/string stringValue = this.m_StringValue;
if (this.m_currentThread != Thread.InternalGetCurrentThread()) // Вот здесь если IntPtr.Zero идет копирование строки
{
/*
Это происходит в потоке, "чужом" по отношению к тому, который сейчас модифицирует строку.
Другой случай - никто ничего не модифицирует, просто висит буфер с какими-то оставшимися данными.
*/return string.InternalCopy(stringValue);
}
if ((2 * stringValue.Length) < stringValue.ArrayLength)
{
/*
А вот это - в том потоке, который сейчас занят модификацией.
Если буфер заполнен меньше, чем на половину, то копируем строку.
Маркер "продолжающейся модификации" при этом не снимается.
*/return string.InternalCopy(stringValue);
}
/*
Если буфер заполнен больше, чем на половину, имеется активная модификация, и всё это происходит в потоке,
осуществляющем эту модификацию, то считаем, что этим вызовом ToString текущая модификация заканчивается.
Вот такая эвристика.
*/
stringValue.ClearPostNullChar();
this.m_currentThread = IntPtr.Zero;
return stringValue;
}
ClearPostNullChar() вероятно, нужен для оптимизации по памяти.
В сумме, если это код FW-шного StringBuildera, то понятно, что он заточен на быструю отдачу строки при условии, что заполнено не менее половины буфера. То есть, EnsureCapacity(очень много) может привести к обратным результатам — StringBuilder будет считать, что всё ещё только начинается.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!