+83
- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
private String toHTML(String unicode)
{
String output = "";
char[] charArray = unicode.toCharArray();
for (int i = 0; i < charArray.length; ++i)
{
if ((int)charArray[i]>255)
{
String s = ""+Integer.toHexString(charArray[i]);
switch (s.length())
{
case 1: s="\\u000"+s; break;
case 2: s="\\u00"+s; break;
case 3: s="\\u0"+s; break;
case 4: s="\\u"+s; break;
default: throw new RuntimeException( s +" is tool long to be a Character");
}
output += s;
}
else
{
output += charArray[i];
}
}
return output;
}
Эпичнейший говнокод! На серваке top показывает нагрузку 10-12. 3000 пользователей, 100 нод, интеграция с SAP, который пачками проводит документы и выдаёт цены, отчёты по остаткам и т.п. И всё это, как оказалось, капля в море по сравнению с 5 человеками техподдержки, которые сидят в аяксовой консоле мониторинга, для которой HTTP-ответ экранируется данным шедевром. Без этого шедевра нагрузка держится в районе 2-3 даже при достаточно большой активности.
Запостил: konsoletyper,
26 Июня 2012
bormand 26.06.2012 14:06 # +1
> output += charArray[i]
Бедный GC... за что они его так...
konsoletyper 26.06.2012 14:14 # +1
bormand 26.06.2012 14:18 # +1
А куда ему деваться? Стринг билдер или его аналог он всяко не додумается вставить. Так и будет конкатенировать до посинения ;)
konsoletyper 26.06.2012 14:24 # −2
bormand 26.06.2012 15:08 # +2
roman-kashitsyn 26.06.2012 15:10 # +3
bormand 26.06.2012 15:20 # +1
guest 26.06.2012 15:16 # +3
guest 26.06.2012 16:46 # 0
TarasB 26.06.2012 14:20 # 0
Там амортизация вшита, если что.
carsten 26.06.2012 14:22 # 0
TarasB 26.06.2012 14:26 # 0
guest 26.06.2012 14:28 # +1
TarasB 26.06.2012 14:33 # +2
roman-kashitsyn 26.06.2012 15:21 # +2
Выход, конечно, есть - копировать строки при использовании их в качестве ключей. Проблема в том, что копирование будет происходить даже в том случае, если строки никто менять не собирался.
Более изящний выход - сделать строки иммутабельными, тогда и копировать ничего не нужно, и сломать массив никто не сможет. А чтобы собирать новые строки, нужно использовать буфер в явном виде.
Второй вариант безопаснее, особенно в контексте средней квалификации программистов на жабе.
TarasB 26.06.2012 15:32 # 0
Ну допишет, и как это скажется на ассоциативном массиве? В Дельфах коровьи строки ващето. А в крестах std::string вообще копируется на каждое =, и тоже мутабелен, какой ужас.
roman-kashitsyn 26.06.2012 15:42 # 0
Ну там всего скорее copy on first write.
А поддержание такого механизма в многопоточной среде сложнее и рискованней, чем тупо сделать строки иммутабельными. Да и реализовать такой String на чистой жабе (там только один нативный метод - intern, появившийся относительно недавно) не получилось бы, перегрузки операторов нет.
defecate-plusplus 26.06.2012 15:48 # +1
именно из-за
> поддержание такого механизма в многопоточной среде
ну и из-за того, что ты всегда можешь залезть грязными руками в содержание строки и чего-нибудь да поменять, никакой COW механизм это не отследит
если так хочется хардкора (а для больших строк периодически хочется), то тупо boost::shared_ptr<some_big_object> и все счастливы
по факту спасает непосредственно то, что это какая то спарта передавать string & дальше в обработку, ведь в 95% случаев строка создается один раз и возится по коду через const &
roman-kashitsyn 26.06.2012 16:50 # +2
Собственно, вернулись в исходную точку. Получается, что мапы, к примеру, должны делать копии ключей, даже когда это не нужно.
Кстати, от иммутабельный строк есть ещё один бенефит в контексте ассоциативных массивов - для них можно кэшировать значение хэш-функции, что позволит рассчитывает его один раз на объект.
defecate-plusplus 26.06.2012 17:01 # +1
ну и т.к. unmanaged code - хочу в своем потоке копирую строку, хочу меняю - остальные потоки это никак не затрагивает, строка вообще может быть на стеке (в реализации msvs строки до 16 символов живут внутри объекта, а не в куче) или в TLS - никакой гц никто за ниточки не дергает
а хеш по строке считается не так медленно, чтобы ради этого настолько сильно переживать
в любом случае, в с++ невозможно будет красиво его держать в актуальном состоянии - та же проблема, что и с COW - строки-то мутабельные - вася пупкин делает str[13] = 'ъ', а петя жопкин вообще берёт и теребит свой (char *)&str[0], и всё, давай до свидания - никто не отследит момент, когда хеш пора перерасчитывать
defecate-plusplus 26.06.2012 17:08 # +1
всё упирается в наличие или отсутствие управляемой среды
есть - значит можно фантазию проявлять, глобально считать использование объектов, запрещать запись туда, где нельзя и т.д.
нет - ну что ж поделать, пистолет дали, крутись как хочешь
UncleAli 27.06.2012 14:07 # +1
defecate-plusplus 27.06.2012 14:42 # 0
HaskellGovno 26.06.2012 17:06 # +1
Вон иммутабильная строка в 100500 символов и используется только в одном месте кода, а потом вдруг решил её поменять немного для использования в том же самом месте и делаешь лишнюю копию всей длиннющей строки в 100500 символов. Хватит с этими двойными стандартами. Вон вроде строка иимутабильная, а копию лишнюю делаешь.
guest 26.06.2012 17:08 # 0
bormand 26.06.2012 17:14 # 0
Так это при мутабельных строках.
>Вон вроде строка иимутабильная, а копию лишнюю делаешь.
Здесь еще одна фишка есть - за любые изменения иммутабельной строки платит только тот, кто ее меняет. Но за возможность менять мутабельную строку платят все, кто ее юзает ее копии. Видимо разработчики явы оценили, какие случаи чаще - и сделали строки иммутабельными.
roman-kashitsyn 26.06.2012 17:26 # 0
Однако есть другой иммутабельный тип, для которого эта особенность стреляет - BigInteger/BigDecimal. Вроде делать число мутабельным - грех, а на больших числах производительность из-за иммутабельности серьёзно проседает.
3.14159265 27.06.2012 21:16 # +1
Хочешь юзай немутабельные Стринги, хочешь мутабельный StringBuffer (причем изначально в JDK 1.0 сделали они потокобезопастным).
И только гораздо позже из практических соображений добавили thread-unsafe StringBuilder.
Тут расчет на то что кодер, он не тупая обезьянка и знает чего хочет. Где ему выгодней немутабельное - он будет юзать его.
В пользу того что решения кстати говорит и то что в шарпе полностью переняли подход.
3.14159265 27.06.2012 21:19 # +1
За ссылки не парься - объекты немутабельны - хер поменяешь.
Треды? Да всё ништяк. Оно само распаралелится. Итд.
Вот в таком случае (когда компилятор и среда исполнения предполагаются умнее чем программист) то этими проблемами и должны заниматься они.
Как я описал ниже VM должна быть умной:
- видишь что применением функции из одного объекта получаем другой, а этот уже никому не нужен - не выделяй память, а модифицируй по возможности старый и подсунь ссылку на него.
Lure Of Chaos 28.06.2012 13:01 # +1
это если длина совпадает байт-в-байт. собственно, предполагаю, все современные VM так и делают.
3.14159265 02.07.2012 16:43 # 0
Нет, я говорю о самом общем случае мутации.
Вот взять этот пример:
for (){
output += x;
}
Единственная ссылка на старый output удаляется и заменяется ссылкой на новый объект строки.
Если компилятор видит такую ситуацию - он ставит в байт-коде на типе строки какой-то хинт типa Mutable на CharArray и выделяет память с избытком (например 2^n).Тогда в строку можно добавлять данные спереди и сзади (надо только дополнительно хранить позицию начала) и при переполнении расширять до 2^(n+1).
И при любых мутациях - append, prepend, delete, write (разве что кроме inserta) мы просто меняем и снова используем старый объект и даже ссылку на output менять не надо.
Возможно строки в яве - не очень удачный пример, но вот в функциональных языках где немутабельные объекты то и дело неявно создаются и собираются GC такого рода оптимизации уже неиспользуемых обектов сделать вполне реально.
TarasB 26.06.2012 18:56 # +2
bormand 26.06.2012 19:02 # 0
bormand 26.06.2012 15:26 # +3
Очень шустрые substr'ы.
Экономия памяти на одинаковых строках и их кусках без накладных расходов на копирование или подсчет ссылок.
Достаточно быстрая конкатенация (за O(длины результата) если делать ее не через жопу).
roman-kashitsyn 26.06.2012 15:29 # +2
Тут и downside есть: вытянул документище в память, взял у него substr(x, x+5), и выбросил. А ссылка на документище всё ещё болтается в этом substr'е. Но это не так часто бывает и довольно легко решается.
bormand 26.06.2012 15:35 # +1
Ничего идеального в этом мире нет ;(
3.14159265 27.06.2012 20:56 # +1
И не хотел влезать, но.
>Тут и downside есть: вытянул документище в память, взял у него substr(x, x+5), и выбросил.
>А ссылка на документище всё ещё болтается в этом substr'е.
Возможно это пахнет фантастикой, но ведь можно сделать так чтобы GC освобождал страницы памяти с неиспользуемыми более кусками массивов.
То есть считать не только недоступные объекты, но и страницы (в случае больших массивов). Это кстати поможет не только подстрокам, но подлистам на основе массивов.
То есть это проблемы сборщика - что он тупой недостаточно умный.
>Например иммутабельная строка плохо подходит для множества мелких изменений.
Снова-таки возможно это пахнет фантастикой, но если мы заведомо знаем, что наша строка имеет только одну ссылку, и эта ссылка убирается, но в то же время создается похожий новый объект, мы внутри виртуальной машины можем просто заменить эту операцию модификацией исходного объекта.
Это касается того случая когда мы видели адские цифры на permutationах в Хацкиле, а ведь будь оно достаточно умным - могло бы просто модифицировать старый список.
Опять-таки это недостаточно умная среда исполнения, а не проблема иммутабельных объектов.
bormand 27.06.2012 21:12 # +2
В хаскеле вроде бы так lazy bytestring сделан. Строка записана в виде списка иммутабельных сегментов, которые могут подкачиваться по требованию и уничтожаться GC по частям, если эти части не нужны.
3.14159265 27.06.2012 21:21 # 0
Lure Of Chaos 28.06.2012 13:09 # +2
а то в той же жабе полно западла: и вот тут многие думают, что сборщик сможет собрать мегадлинную строку, оставив маинькую субстроку - а нифига вот только так всё будет хорошо
roman-kashitsyn 28.06.2012 13:17 # +2
guest 26.06.2012 16:54 # 0
В большинстве приложений строки функционально это базовый примитивный тип, который на практике имеет семантику by value. Прикинь, что было бы, если бы, скажем, число Integer всегда было ссылочным. Какой-то поток в каком-то месте изменил число с 7 на 1 -- и это везде отразилось во всех участках кода, куда число затесалось. А отдебагить сложно, куда затесалось, куча геморроя. Поэтому разумно по умолчанию делать такие объекты неизменяемыми. Этакая симуляция byvalue в контексте иерархии ссылочных объектов.
TarasB 26.06.2012 18:58 # 0
bormand 26.06.2012 19:03 # 0
TarasB 26.06.2012 19:09 # 0
Думаю да. Для интерфейсов вот счётчик ссылок сделан через какие-то InterlockedIncrement, видимо это для потоков фича, значит и для строк должно быть.
bormand 26.06.2012 19:10 # 0
guest 26.06.2012 19:11 # +2
TarasB 26.06.2012 19:13 # +1
guest 26.06.2012 19:21 # −2
TarasB 26.06.2012 19:22 # +1
bormand 26.06.2012 19:22 # +1
roman-kashitsyn 27.06.2012 13:16 # 0
которые требуют атамарного инкремента счётчиков даже при копировании в рамках одного потока. Получается, что мы платим за многопоточность (если, конечно, не используются atomic типы современных процессоров, сомневаюсь, что в Delphi это есть) даже тогда, когда её не используем.
TarasB 27.06.2012 13:22 # 0
И вообще строки не то место, где надо экономить. Кому надо быстро - кастуют к пчару.
roman-kashitsyn 27.06.2012 13:28 # 0
TarasB 27.06.2012 13:53 # 0
bormand 27.06.2012 13:44 # 0
1) Во многих случаях строку передают по цепочке функций как const &.
2) В тех же случаях когда нужна именно копия объекта - атомарный инкремент обойдется дешевле выделения памяти и полного копирования (если строки не по 3 символа).
А в делфи, как кидал выше Тарас, используется InterlockedIncrement. Т.е. тоже atomic int.
roman-kashitsyn 27.06.2012 13:48 # 0
Если атомарный, то всё ок
Оба подхода имеют право на жизнь, не спорю. Просто для того, чтобы делать строки иммутабельными, тоже есть веские причины.
bormand 27.06.2012 13:54 # 0
carsten 27.06.2012 22:19 # −2
Атомарный инт только гарантирует, что число атомарно будет изменяться -- и всё. Дальше мы сравниваем число с нулём и джампим -- и тут -то оно уже не атомарно. Другой поток может "включиться" в середине сравнения с нулём и повредить объект. Короче, мороки много.
bormand 28.06.2012 05:36 # −1
Не только. Атомарные операции еще и атомарно возвращают новое или старое значение (смотря какая операция), в противном случае они были бы просто бесполезны.
man атомарные операции
man барьеры и memory ordering
carsten 28.06.2012 16:03 # −1
Мне кажется, конкатенация строк происходит значительно реже, чем копирование объекта в С++, поэтому мне не понятно зачем оверхедить на каждый шаг (а С++ копирует на каждом шагу по 500 раз) только потому, что строку теоретически могут конкатенировать где-то раз в год? Правильнее всё-таки сделать строку полностью иммутабельной -- универсально простое решение, с нулём оверхеда при передаче объекта; и вот уже построитель строк -- стрингбилдер -- пусть владеет shared-семантикой.
bormand 28.06.2012 16:11 # +1
> с нулём оверхеда при передаче объекта
А удалять эти самые иммутабельные строки в с++ кто будет? Пушкин?
rat4 28.06.2012 17:31 # 0
bormand 28.06.2012 17:36 # 0
С каким-нибудь атомарным счетчиком ссылок... От чего уходили к тому и вернулись ;)
carsten 28.06.2012 18:26 # 0
carsten 28.06.2012 18:25 # 0
bormand 28.06.2012 20:04 # 0
> shared_ptr позволяет самому решать.
Отключением поддержки тредов? Так и COW строки при отключении трединга перейдут на использование обычных операций вместо атомарных.
Или, может быть, неиспользованием shared_ptr, там где очевидно, что строка будет в одном экземпляре - так и COW строки получат оверхед только в конструкторе и деструкторе, где крутятся new и delete, на фоне недетерминированного оверхеда которых, две атомарных операции будут незаметны.
И да, COW строки, так же как и std::string и иммутабельные строки можно передавать по const & в вызываемые функции. Здесь оверхеда нет при любой реализации.
А теперь, давайте, от оверхедов перейдем к семантике. Ведь, сейчас не 80е годы, и время программиста, рабающего над кодом стало намного дороже машинного времени. И здесь COW структуры данных представляют возможность реализовать нужный алгоритм, не заморачиваясь на такие мелочи как выделение и освобождение памяти, и где передать копию, а где шаред поинтер... А ведь код без этих преждевременных оптимизаций легче рефакторить и поддерживать. А это, имхо, почти всегда важнее оверхеда.
P.S. Я ничего не имею против иммутабельных строк и std::string. Просто мне неприятно когда COW поливают говном и не видят в нем никаких плюсов.
HaskellGovno 28.06.2012 23:40 # 0
Личное оскорбление. Тарас, будь моим секундантом.
TarasB 29.06.2012 13:04 # +1
TarasB 28.06.2012 22:54 # +3
ты хуй
bormand 26.06.2012 15:10 # 0
roman-kashitsyn 26.06.2012 15:31 # +1
bormand 26.06.2012 15:36 # 0
konsoletyper 26.06.2012 16:05 # +3
bormand 26.06.2012 16:12 # 0
Имхо надо бы еще поменять 255 на 127. Маловероятно, что в юникодной строке попадутся символы с кодами 128-255, но я бы подстраховался.
konsoletyper 26.06.2012 16:38 # 0
roman-kashitsyn 26.06.2012 17:24 # +1
Да вроде бы нет, не заэкранирует.
Я пробовал как-то без указания кодировки (utf-8) gson.toString() в поток выводить, не работает.
konsoletyper 26.06.2012 22:15 # 0