Re[44]: Ну ты вообще многого не видишь... ;)
От: Klapaucius  
Дата: 08.06.09 16:42
Оценка:
2 criosray: Толку-то в СВ баллы ставить? Если вам этот код по какой-то причине понравится — можете поставить где-нибудь в профильном баллы пользователю Alexey Romanov. Это его проект.
... << RSDN@Home 1.2.0 alpha 4 rev. 1228>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[45]: Ну ты вообще многого не видишь... ;)
От: criosray  
Дата: 08.06.09 16:46
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>2 criosray: Толку-то в СВ баллы ставить? Если вам этот код по какой-то причине понравится — можете поставить где-нибудь в профильном баллы пользователю Alexey Romanov. Это его проект.


Баллы за ссылку, т.к. упорно гуглил, но так и не нашел.
Re[42]: Ну ты вообще многого не видишь... ;)
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.06.09 16:54
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, Serginio1, Вы писали:


S>>На дефрагментацию кучи время уходит? И это может происходить достаточно часто. Все зависит от емкости, и скорости изменения дерева.

WH>Чё? Дефрагментация кучи происходит во время сборки мусора.
WH>Причем я тут можно очень серьезно срезать углы по сравнения с изменяемой кучей.
По сравнению с изменяемой кучей, одноразмерных объектов, может даже и сливать.
WH>>>2-3 дерево. Частный случай B дерева. Еще вопросы?
S>> Прибавляй затраты на балансировку.
WH>Какую балансировку?
WH>B дерево строится сбалансированным сразу.
WH>Ты вообще представляешь как работают B деревья?
Знаю. http://rsdn.ru/article/alg/tlsd.xml
Автор(ы): Сергей Смирнов (Serginio1)
Дата: 14.08.2004
Пример реализации двухуровневого массива с помощью нового средства С# — generics. Сравнение производительности различных реализаций сортированных списков.
. Я имел ввиду затраты на новые узлы.
Если ссылки на строки лежат в листьях тому, что объект занимает 12 бай + лево + право + сумма итого 28 байт на объект
Плюс листья по 3 ссылки на строки итого 12 байт. При частой сборке мусора они будут вносить свою лепту в фрагмнтацию кучи старших поколений.
Мало того, что ты получаешь дефрагментацию в 0 поколении, так еще получишь и в старших поколениях.

При слиянии строк данные как правило сидят в кэше, так то на копирование другие трудозатраты.
S>> Я к тому, что много дополнительных затрат, которые по уму нужно учитывать, при выборе типа инструмента.
WH>Каких?
На дефрагментацию старших поколений. Плюс при сканировании строки скачки по уровням, и тормоза к памяти вне кэша, по сравненю с прогоном по непрерывной памяти.
Мало того, в большинсве случаем строки выделяются сразу, не подвергаются изменениям в течении всей своей жизни.
Во всяком случае на 64 элементах Б+ деревьях это заметно.
S>>Поэтому и убежден, что 3 на бора строк это лучше чем 1 универсальный,
WH>Хуже. Причем сильно хуже.
Ну Б+ деревьев до сих пор в стандартной библиотеке нет, а SortedList сливает также как и стрингБуилдеру веревкам.
Посмотрим когда их в библиотеку. Затраты по сравнению с обычными строками я приводил.

S>>но с реализацией одинаковых интерфейсов.

WH>Одинаковые интерфейсы у изменяемых и неизменяемых структур данных просто сюр.
Ну не совсем. Я имел ввиду имутабельные строки один интерфейс для мутабельных другой, но если для первых это функции то для вторых это процедуры.
и солнце б утром не вставало, когда бы не было меня
Re[50]: Ну ты вообще многого не видишь... ;)
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 08.06.09 16:56
Оценка:
Здравствуйте, criosray, Вы писали:

C>C++ 858 ms

C>C# 823 ms

Это называется "равны с точностью до погрешности измерения".

C>Про сложность кода и явную костыльность С++ (обратите внимание как пришлось извратиться, чтоб сконвертировать число в wstring там, где StringBuilder делает конвертацию сам и не парит нам мозги) и говорить не стоит — вся красота перед глазами.


Мне из моего колодца трудно судить о недостаточной эстетике, я, так сказать, привык по-простому, "от сохи":
ws += L"Карл у Клары украл кораллы в количестве ";
ws += _itow(i,buffer,10);
ws += L" штук\n";


И не надо никаких append и преобразований из MBCS.

C>Но самое замечательное, что С++ вариант банально крашился, если Count = 10000 (и возможно меньше... не проверял между 5к и 10к).


У меня было всё ровно наоборот: C++ вариант попыхтел на пару с виндой, но таки 10K строк отработал, тогда как C# пыхтел столько же, а потом сказал OutOfMemoryException.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[63]: Ну ты вообще многого не видишь... ;)
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.06.09 17:01
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>Здравствуйте, Serginio1, Вы писали:


K>>>Это для вставки. А не для добавления к началу/концу т.е. конкатенации.

S>>Может я чего то не понимаю — В чем разница? Так структура мутабельна мы должны выделить новыю ссылку на корень,и от него добраться либо в конец либо в начало,
S>>выделяя новую ветку, с модификациу длины?

K>1) Для конкатенации не нужно добираться в конец или в начало.

K>2) Быстрый доступ к концу и началу можно организовать с помощью двух "пальцев" указывающих на самый левый и самый правый узлы (удерживать их всегда в первом уровне дерева). Но для конкатенации это не нужно, это нужно если мы хотим чтобы строки у нас были деком с константным доступом.
эээ WolfHound утверждает что веревки это 2-3 дерево. Так что вставка не зависит от того в какое место вставляется, это не очередь.


S>>По уму нужно еще сложность балансировки добавлять, т.к. деревья могут вырождаться в двухнапрвленный список.


K>Это все учтено. Сложность вставки О(1) — амортизированная.

Наверное я чего то не пониаю. Ну да ладно
и солнце б утром не вставало, когда бы не было меня
Re[51]: Ну ты вообще многого не видишь... ;)
От: criosray  
Дата: 08.06.09 17:02
Оценка: -1 :)
Здравствуйте, Геннадий Васильев, Вы писали:


C>>C++ 858 ms

C>>C# 823 ms

ГВ>Это называется "равны с точностью до погрешности измерения".


Конечно. Но вот чисто append сливает в три раза, а чисто replace так вообще в десятки раз (по крайней мере наколенковая реализация на базе wstring.find/wstring.replace.

C>>Про сложность кода и явную костыльность С++ (обратите внимание как пришлось извратиться, чтоб сконвертировать число в wstring там, где StringBuilder делает конвертацию сам и не парит нам мозги) и говорить не стоит — вся красота перед глазами.


ГВ>Мне из моего колодца трудно судить о недостаточной эстетике, я, так сказать, привык по-простому, "от сохи":

ГВ>
ГВ>ws += L"Карл у Клары украл кораллы в количестве ";
ГВ>ws += _itow(i,buffer,10);
ГВ>ws += L" штук\n";
ГВ>


ГВ>И не надо никаких append и преобразований из MBCS.

Принято.

C>>Но самое замечательное, что С++ вариант банально крашился, если Count = 10000 (и возможно меньше... не проверял между 5к и 10к).


ГВ>У меня было всё ровно наоборот: C++ вариант попыхтел на пару с виндой, но таки 10K строк отработал, тогда как C# пыхтел столько же, а потом сказал OutOfMemoryException.

У меня дотнет вариант отработал за примерно 4 секунды, С++ тут же крашнулся без вменяемого сообщения о роде проблемы.
Re[64]: Ну ты вообще многого не видишь... ;)
От: Klapaucius  
Дата: 08.06.09 17:11
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> эээ WolfHound утверждает что веревки это 2-3 дерево.


Ну, не обязательно 2-3.

K>>Сложность вставки О(1) — амортизированная.

S> Наверное я чего то не пониаю. Ну да ладно

Я оговорился, конечно. Конкатенация — константная (амортизированная). Вставка — логарифмическая. Как я выше по ветке и говорил.
... << RSDN@Home 1.2.0 alpha 4 rev. 1228>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[52]: Ну ты вообще многого не видишь... ;)
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 08.06.09 17:18
Оценка:
Здравствуйте, criosray, Вы писали:

C>>>C++ 858 ms

C>>>C# 823 ms

ГВ>>Это называется "равны с точностью до погрешности измерения".


C>Конечно. Но вот чисто append сливает в три раза, а чисто replace так вообще в десятки раз (по крайней мере наколенковая реализация на базе wstring.find/wstring.replace.


Тут на самом деле не надо путать StringBuilder, специально заточенный на модификации, и std::wstring, которая, по сути, своего рода компромисс между хранилищем строки (добавь требование преобразования к zero-terminated) и StringBuilder. Забавляет другое — что некоторый выигрыш Append/Replace нивелируется медленным ToString.

C>>>Но самое замечательное, что С++ вариант банально крашился, если Count = 10000 (и возможно меньше... не проверял между 5к и 10к).

ГВ>>У меня было всё ровно наоборот: C++ вариант попыхтел на пару с виндой, но таки 10K строк отработал, тогда как C# пыхтел столько же, а потом сказал OutOfMemoryException.
C>У меня дотнет вариант отработал за примерно 4 секунды, С++ тут же крашнулся без вменяемого сообщения о роде проблемы.

На 10000 строк 4 секунды? Интересно.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[53]: Ну ты вообще многого не видишь... ;)
От: criosray  
Дата: 08.06.09 17:26
Оценка: +1
Здравствуйте, Геннадий Васильев, Вы писали:


C>>>>C++ 858 ms

C>>>>C# 823 ms

ГВ>>>Это называется "равны с точностью до погрешности измерения".


C>>Конечно. Но вот чисто append сливает в три раза, а чисто replace так вообще в десятки раз (по крайней мере наколенковая реализация на базе wstring.find/wstring.replace.


ГВ>Тут на самом деле не надо путать StringBuilder, специально заточенный на модификации, и std::wstring, которая, по сути, своего рода компромисс между хранилищем строки (добавь требование преобразования к zero-terminated) и StringBuilder.

О том и речь. Потому я и утверждал, что разделение на immutable класс строк и mutable класс аккумулятор строк позволяют достич значительно большей эфективности.

ГВ>Забавляет другое — что некоторый выигрыш Append/Replace нивелируется медленным ToString.

Обычно ToString вызывается гораздо реже Append`ов и крайне редко, чтоб он вызывался в цикле, как было предложено.

C>>>>Но самое замечательное, что С++ вариант банально крашился, если Count = 10000 (и возможно меньше... не проверял между 5к и 10к).

ГВ>>>У меня было всё ровно наоборот: C++ вариант попыхтел на пару с виндой, но таки 10K строк отработал, тогда как C# пыхтел столько же, а потом сказал OutOfMemoryException.
C>>У меня дотнет вариант отработал за примерно 4 секунды, С++ тут же крашнулся без вменяемого сообщения о роде проблемы.

ГВ>На 10000 строк 4 секунды? Интересно.

Да чуть меньше 4х. На 5000 меньше секунды. С2D 3Ghz, 6GB RAM, Win7 64bit.
Re[53]: Ну ты вообще многого не видишь... ;)
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.06.09 17:30
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

почувсвуйте разницу на коротких строках. Поэтому и пишут свои стрингбуилдеры
public override string ToString()
{
    string stringValue = this.m_StringValue;
    IntPtr currentThread = this.m_currentThread;
    if ((currentThread != IntPtr.Zero) && (currentThread != InternalGetCurrentThread()))
    {
        return string.InternalCopy(stringValue);
    }
    if ((2 * stringValue.Length) < stringValue.ArrayLength)
    {
        return string.InternalCopy(stringValue);
    }
    stringValue.ClearPostNullChar();
    this.m_currentThread = IntPtr.Zero;
    return stringValue;
}




public string ToString(int startIndex, int length)
{
    return this.m_StringValue.Substring(startIndex, length);
}
и солнце б утром не вставало, когда бы не было меня
Re[53]: Ну ты вообще многого не видишь... ;)
От: hattab  
Дата: 08.06.09 17:43
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>>>Это называется "равны с точностью до погрешности измерения".


C>>Конечно. Но вот чисто append сливает в три раза, а чисто replace так вообще в десятки раз (по крайней мере наколенковая реализация на базе wstring.find/wstring.replace.


ГВ>Тут на самом деле не надо путать StringBuilder, специально заточенный на модификации, и std::wstring, которая, по сути, своего рода компромисс между хранилищем строки (добавь требование преобразования к zero-terminated) и StringBuilder. Забавляет другое — что некоторый выигрыш Append/Replace нивелируется медленным ToString.


Не флейма ради В Delphi самые православные (и даже zero-terminated совместимые) строки По первому тесту результаты такие (только я количество итераций уменьшил на один нолик, а то у меня памяти не хватает ):

C#:
append: elapsed 00:00:00.6044270
20000000
replace: elapsed 00:00:00.8633580
20000000
insert: elapsed 00:00:06.7491980
20000090

Delphi 2009 (миллисекунды):
----------- RTL строки
string.append: elapsed 697 (20000000)
string.replace: elapsed 0 (20000000) // нет замены в строке, только с созданием копии -- мерять не стал
string.insert: elapsed 5880 (20000090)
----------- с использование класса TStringBuilder
sb.append: elapsed 1063 (20000000)
sb.replace: elapsed 65 (20000000)
sb.insert: elapsed 5862 (20000090)

Код:
Var

 sw    : TStopwatch; // QueryPerformanceCounter
 s     : String; // это юникод
 sb    : TStringBuilder;
 Index : Integer;

Begin

 sw.Start;

 For Index := 1 To 10000000 Do
  s := s + 'c1';

 sw.Stop;

 WriteLn('string.append: elapsed ', sw.ElapsedTime, ' (', Length(s), ')');

 sw.Start;
// s := StringReplace(s, '1', '2', [rfReplaceAll]);
 sw.Stop;

 WriteLn('string.replace: elapsed ', sw.ElapsedTime, ' (', Length(s), ')');

 sw.Start;

 Index := 10000;
 While Index < 100000 Do
  Begin

   Insert('7', s, Index);
   Inc(Index, 1000);

  End;

 sw.Stop;

 WriteLn('string.insert: elapsed ', sw.ElapsedTime, ' (', Length(s), ')');

 WriteLn('-----------');

 sb := TStringBuilder.Create;
 Try

  sw.Start;

  For Index := 1 To 10000000 Do
   sb.Append('c1');

  sw.Stop;

  WriteLn('sb.append: elapsed ', sw.ElapsedTime, ' (', sb.Length, ')');

  sw.Start;
  sb.Replace('1', '2');
  sw.Stop;

  WriteLn('sb.replace: elapsed ', sw.ElapsedTime, ' (', sb.Length, ')');

  sw.Start;

  Index := 10000;
  While Index < 100000 Do
   Begin

    sb.Insert(Index, '7');
    Inc(Index, 1000);

   End;

  sw.Stop;

  WriteLn('sb.insert: elapsed ', sw.ElapsedTime, ' (', sb.Length, ')');

 Finally

  sb.Free;

 End;

End.
Re[54]: Ну ты вообще многого не видишь... ;)
От: criosray  
Дата: 08.06.09 17:45
Оценка:
Здравствуйте, Serginio1, Вы писали:


S>почувсвуйте разницу на коротких строках. Поэтому и пишут свои стрингбуилдеры


Кстати, спасибо, что напомнили. Публичные методы StringBuilder еще и thread safe в отличии от std::wstring.
Re[54]: Ну ты вообще многого не видишь... ;)
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.06.09 17:50
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, Геннадий Васильев, Вы писали:


S>почувсвуйте разницу на коротких строках. Поэтому и пишут свои стрингбуилдеры

S>
S>public override string ToString()
S>{
S>    string stringValue = this.m_StringValue;
S>    IntPtr currentThread = this.m_currentThread;
S>    if ((currentThread != IntPtr.Zero) && (currentThread != InternalGetCurrentThread()))
S>    {
S>        return string.InternalCopy(stringValue);
S>    }
S>    if ((2 * stringValue.Length) < stringValue.ArrayLength)
S>    {
S>        return string.InternalCopy(stringValue);
S>    }
S>    stringValue.ClearPostNullChar();
S>    this.m_currentThread = IntPtr.Zero;
S>    return stringValue;
S>}
S>


При коротких строках лишние сравнения,
на длиных строках если не выполняется второе условие то
вступает всилу обнуление строки

internal unsafe void ClearPostNullChar()
{
    int index = this.Length + 1;
    if (index < this.m_arrayLength)
    {
        fixed (char* chRef = &this.m_firstChar)
        {
            chRef[index] = '\0';
        }
    }
}





S>
S>public string ToString(int startIndex, int length)
S>{
S>    return this.m_StringValue.Substring(startIndex, length);
S>}
S>
и солнце б утром не вставало, когда бы не было меня
Re[54]: Ну ты вообще многого не видишь... ;)
От: criosray  
Дата: 08.06.09 17:55
Оценка:
Здравствуйте, hattab, Вы писали:


H>Delphi 2009 (миллисекунды):

H>----------- RTL строки
H>string.append: elapsed 697 (20000000)
H>string.replace: elapsed 0 (20000000) // нет замены в строке, только с созданием копии -- мерять не стал
H>string.insert: elapsed 5880 (20000090)
H>----------- с использование класса TStringBuilder
H>sb.append: elapsed 1063 (20000000)
H>sb.replace: elapsed 65 (20000000)

Попробуйте Replace('1','2') заменить на Replace('c1', 'abc4') Будет куда интереснее, чем замена одного символа.
Re[55]: Ну ты вообще многого не видишь... ;)
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 08.06.09 18:08
Оценка:
Здравствуйте, Serginio1, Вы писали:

Извини, я не совсем понял — к чему ты это? Сам код понятен, не могу понять твою мысль.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[54]: Ну ты вообще многого не видишь... ;)
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 08.06.09 18:19
Оценка:
Здравствуйте, criosray, Вы писали:

ГВ>>>>Это называется "равны с точностью до погрешности измерения".

C>>>Конечно. Но вот чисто append сливает в три раза, а чисто replace так вообще в десятки раз (по крайней мере наколенковая реализация на базе wstring.find/wstring.replace.
ГВ>>Тут на самом деле не надо путать StringBuilder, специально заточенный на модификации, и std::wstring, которая, по сути, своего рода компромисс между хранилищем строки (добавь требование преобразования к zero-terminated) и StringBuilder.
C>О том и речь. Потому я и утверждал, что разделение на immutable класс строк и mutable класс аккумулятор строк позволяют достич значительно большей эфективности.

Нет, с иммутабельностью это не связано. Здесь главное то, что StringBuilder специально затачивался на модификацию строк, а std::wstring — это так, гора компромиссов между ужами и ежами.

ГВ>>Забавляет другое — что некоторый выигрыш Append/Replace нивелируется медленным ToString.

C>Обычно ToString вызывается гораздо реже Append`ов и крайне редко, чтоб он вызывался в цикле, как было предложено.

Цикл нужен только для того, чтобы время операции точней померить.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[55]: Ну ты вообще многого не видишь... ;)
От: criosray  
Дата: 08.06.09 18:33
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:


ГВ>>>>>Это называется "равны с точностью до погрешности измерения".

C>>>>Конечно. Но вот чисто append сливает в три раза, а чисто replace так вообще в десятки раз (по крайней мере наколенковая реализация на базе wstring.find/wstring.replace.
ГВ>>>Тут на самом деле не надо путать StringBuilder, специально заточенный на модификации, и std::wstring, которая, по сути, своего рода компромисс между хранилищем строки (добавь требование преобразования к zero-terminated) и StringBuilder.
C>>О том и речь. Потому я и утверждал, что разделение на immutable класс строк и mutable класс аккумулятор строк позволяют достич значительно большей эфективности.

ГВ>Нет, с иммутабельностью это не связано. Здесь главное то, что StringBuilder специально затачивался на модификацию строк, а std::wstring — это так, гора компромиссов между ужами и ежами.

Вот именно. Именно такое разделение позволило сделать класс string immutable, а StringBuilder — более эфективным (по производительности, менее эфективным по памяти, очевидно) для операций над строками.

ГВ>>>Забавляет другое — что некоторый выигрыш Append/Replace нивелируется медленным ToString.

C>>Обычно ToString вызывается гораздо реже Append`ов и крайне редко, чтоб он вызывался в цикле, как было предложено.
ГВ>Цикл нужен только для того, чтобы время операции точней померить.

Так что Вы меряете? Append или ToString? Ведь ToString`ов ровно столько же, сколько и Append`ов, что почти что нонсенс.
Re[55]: Ну ты вообще многого не видишь... ;)
От: hattab  
Дата: 08.06.09 18:33
Оценка:
Здравствуйте, criosray, Вы писали:

H>>Delphi 2009 (миллисекунды):

H>>----------- RTL строки
H>>string.append: elapsed 697 (20000000)
H>>string.replace: elapsed 0 (20000000) // нет замены в строке, только с созданием копии -- мерять не стал
H>>string.insert: elapsed 5880 (20000090)
H>>----------- с использование класса TStringBuilder
H>>sb.append: elapsed 1063 (20000000)
H>>sb.replace: elapsed 65 (20000000)

C>Попробуйте Replace('1','2') заменить на Replace('c1', 'abc4') Будет куда интереснее, чем замена одного символа.


Померял -- не дождался
Re[56]: Ну ты вообще многого не видишь... ;)
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.06.09 18:54
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>Здравствуйте, Serginio1, Вы писали:


ГВ>Извини, я не совсем понял — к чему ты это? Сам код понятен, не могу понять твою мысль.

В тормознутости StringBuilder a
и солнце б утром не вставало, когда бы не было меня
Re[42]: Ну ты вообще многого не видишь... ;)
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.06.09 19:01
Оценка:
Здравствуйте, WolfHound, Вы писали:

Я все к чему клоню то. Что если сделать мутабельную структуру,
наподобие Б+ на листовом уровне, и ветки ссумой символов в пддереве как 2-3 дерево, то можно получить
очень быструю структуру. Так в моих Б+ дереве на листовом уровне 64 элемента кей валуе, что для int*int дает отличную скорость,
то для чаров это будет 256. Если вставки и удаления будут такого же порядка, то вполне быстря структура.
Надо будет попробовать на досуге.
и солнце б утром не вставало, когда бы не было меня
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.