GlebZ,
> Стиль от переиспользования характеризуется тем, что при составлении программы стремятся максимально использовать то, что уже сделано — самим программистом, его коллегами или же вообще где-либо. <...>
Не важно это: все равно проектировать заново придется, даже при использовании готовых "блоков" (модулей, компонент и т.п.).
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, GlebZ, Вы писали:
GZ>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Ты меня не понял. Я и не говорил, что rows будут. Я говорил, что Substring будет. GZ>А я говорил, что никаких временных объектов при этом создаваться не будет.
Объясни, пожалуйста , тогда еще раз. Вот твой код
int i, l=0;
while ((i=myString.IndexOf(",", l))!=-1)
{
rows.Add(myString.Substring(i, l-i);
l=i;
}
Здесь при вызове rows.Add ей передается строка, так ? Эта строка должна быть создана , так ? Путем вызова Substring. И все это делается в цикле. Я согласен с тем, что ссылка на эту строку не хранится у тебя, и сразу после Add на эту временную строку ссылок больше нет и она есть мусор для GC, но ведь она все же создается ? Или нет ?
Здравствуйте, Павел Кузнецов, Вы писали:
>> Стиль от переиспользования характеризуется тем, что при составлении программы стремятся максимально использовать то, что уже сделано — самим программистом, его коллегами или же вообще где-либо. <...>
ПК>Не важно это: все равно проектировать заново придется, даже при использовании готовых "блоков" (модулей, компонент и т.п.).
Естественно . Каждый шкаф тоже заново рисуют по пожеланиям клиента.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>> char szTotal[1000]; PD>> char * szStrings[3] ={"abc", "def", "ghi"}; PD>> int nStrings = 3; PD>> char* pCurrent = szTotal; PD>> for(int i = 0; i < nStrings; i++) PD>> pCurrent += sprintf(pCurrent,"%s",szStrings[i]);
S>Гм. Мне не хотелось бы прослыть человеком, приносящим дурные вести, но ты только что продемонстрировал способ внести в программу buffer overrun vulnerability. S>Если не дай байт кто-то изменит содержимое szStrings так, что оно перестанет помещаться в szTotal (который ты предусмотрительно разместил на стеке), то спринтф чудесным образом разрушит стек.
Это уже совсем несерьезно. Человек спрашивал, как технически, т.е. с помощью каких средств языка С++ можно такое сделать. Я ему и показал. Неужели я не понимаю, что 1000 может не хватить ????
S>Это, на мой взгляд, не вполне адекватная плата за однопроходность. S>Еще я бы хотел отметить, что в твоем примере все живет за счет сверхмалых размеров строк. На таких объемах можно хоть перевыделениями заниматься — существенного падения производительности ты не добъешся. А как только мы заговорим о более реалистичном мире, где действительно станет нужна оптимизация строковых операций, выделение фиксированного буфера в стеке моментально исчезнет из поля нашего зрения.
А, прошу прощения, если не знать или хотя бы предполагать общий размер, то задача вообще не разрешима в однопроходном варианте. Потому как нельзя копировать в буфер, не выделив его предварительно. А длины в С++ не хранятся, в отличие от С#. На Паскале можно, там они хранятся.
S>Поэтому настоящие программисты всегда используют нормальный оо-код из std:: вместо всяких спринтфов и прочей небезопасной ерунды.
Нормальные герои всегда идут в обход (C) Доктор Айболит.
S>З.Ы. Кстати, поиск целого в массиве тоже лучше честно делать через S>
gear nuke wrote: > GZ>Мне не интересен сам вопрос про кэш. Кэш неуправляем и эвристичен. Я > предполагаю более управляемую архитектуру. > > По сути, Вы предлагаете управляемый кэш. Это способно дать выигрыш. И > сущесвующие методики работы с кэшем тоже дают выигрыш. Вопрос в том, > *почему* до сих пор нет ни одного компилятора использующего это эффективно?
Современное управление кешем неполно (у кеша всегда есть и своё мнение
кроме prefetchnta) и непереносимо, поэтому никто не заморачивается?
Здравствуйте, McSeem2, Вы писали:
E>>А поиск в Janus я привел как пример распространенного подхода: сделать работоспособный вариант без учета его эффективности. А потом, когда это станет проблемой, оптимизировать.
E>>Тут уже кто-то сказал, что это "потом" никогда не наступает.
MS>Это был я
MS>Но зададим себе вопрос — "а оно надо"? Может быть и не нужен поиск в Janus? Или нужен?
Мне нужен
Иногда возникает какой-нибудь вопрос, который уже затрагивался где-то или просто хочется привести ссылки на какие-то факты. Вот, как здесь: Re: Открыли зачем?
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Это уже совсем несерьезно. Человек спрашивал, как технически, т.е. с помощью каких средств языка С++ можно такое сделать. Я ему и показал. Неужели я не понимаю, что 1000 может не хватить ????
Я не знаю чего ты понимаешь, я не телепат. Но я вижу, что ты привел код, который запрещено применять в коммерческом программировании под страхом смертной казни. Мы приводим такой код только в курсе сетевых технологий, как пример заведомо небезопасного подхода.
PD>А, прошу прощения, если не знать или хотя бы предполагать общий размер, то задача вообще не разрешима в однопроходном варианте. Потому как нельзя копировать в буфер, не выделив его предварительно. А длины в С++ не хранятся, в отличие от С#. На Паскале можно, там они хранятся.
И после этого кто-то будет обвинять C# в неэффективности???
Я приводил пример пессимального кода, который на шарпе приводит к квадратичной зависимости от полной длины строки. С точки зрения того алгоритма, два прохода или один — не так важно. Все равно мы имеем O(N). Даже наличие хранимой длины не слишком изменяет ситуацию — если длины фрагментов примерно одинаковы, то их количество тоже пропорционально суммарной длине строки, и мы опять имеем O(N). Меняется только константа перед ней. S>>а попытки задействовать хардкор типа rep scasd ни к чему хорошему не приводят. PD>Стоит ли его использовать или нет — вопрос спорный, а вот почему не приведут — объясни, пожалуйста.
Видел своими глазами пример. Как знаток x86 — ассемблера страшно раскритиковал код, сгенерированный VC 7.1 в релизе для приведенного мной фрагмента. Действительно, его код был красивше. А студийный — тихий ужос. Какие-то лишние джампы, как-то там регистры странно использовались... Увы, когда мы сравнили время — студия победила. Видать, ее оптимизатор лучше знает особенности спаривания команд и устройство конвеера. После этого я еще немножко верю в то, что суперкрутой спец сможет написать более эффективный код вручную. Но, во-первых, для этого ему потребуется что-то типа Intel VTune, а во-вторых, чем эффективнее будет этот код, тем больше риска, что на другой модели процессора он окажется в дупе. А в компиляторе я просто переброшу ключик "Target Processor" и усё. Кстати, джиту еще лучше — ему не надо оптимизировать под "blend".
А наивные попытки улучшить код, основанные на прочитанной 10 лет назад книжке "Справочное руководство по Intel 80386" способны только просадить эффективность.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Это уже совсем несерьезно. Человек спрашивал, как технически, т.е. с помощью каких средств языка С++ можно такое сделать. Я ему и показал. Неужели я не понимаю, что 1000 может не хватить ???? S>Я не знаю чего ты понимаешь, я не телепат. Но я вижу, что ты привел код, который запрещено применять в коммерческом программировании под страхом смертной казни. Мы приводим такой код только в курсе сетевых технологий, как пример заведомо небезопасного подхода.
Истина всегда конкретна. Если речь идет о неопределенном количестве неизвестной заранее длины строк — согласен. Если же необходимо сконкатенировать вполне предполагаемый набор строк с суммарной длиной, которую можно заранее оценить — ничего криминального не вижу. А оценить ее бывает вполне возможно. И не надо пугать меня смертной казнью — я такое не раз делал в коммерческих приложениях, и они, слава богу, вполне работают. Потому что я прекрасно знал, какой максимальный объем здесь может получиться. Просто потому что больше получится не может — нет такого в той стране, для которой я это делал. Нет, и все. Не бывает и никогда не будет.
PD>>А, прошу прощения, если не знать или хотя бы предполагать общий размер, то задача вообще не разрешима в однопроходном варианте. Потому как нельзя копировать в буфер, не выделив его предварительно. А длины в С++ не хранятся, в отличие от С#. На Паскале можно, там они хранятся. S>И после этого кто-то будет обвинять C# в неэффективности???
Естественно. Оверхед на хранение длины строки, которая чаще всего и не нужга. .
S>Я приводил пример пессимального кода, который на шарпе приводит к квадратичной зависимости от полной длины строки. С точки зрения того алгоритма, два прохода или один — не так важно. Все равно мы имеем O(N).
Я все же полагаю, что AN < 2AN при любом положительном A .
Даже наличие хранимой длины не слишком изменяет ситуацию — если длины фрагментов примерно одинаковы, то их количество тоже пропорционально суммарной длине строки, и мы опять имеем O(N). Меняется только константа перед ней.
Вот именно. Кстати, ты на 100% уверен, к примеру, что AN всегда лучше, чем BNlogN ?
S>Видел своими глазами пример. Как знаток x86 — ассемблера страшно раскритиковал код, сгенерированный VC 7.1 в релизе для приведенного мной фрагмента. Действительно, его код был красивше. А студийный — тихий ужос. Какие-то лишние джампы, как-то там регистры странно использовались... Увы, когда мы сравнили время — студия победила.
Ну и что ?
>Видать, ее оптимизатор лучше знает особенности спаривания команд и устройство конвеера
чем "знаток x86", только и всего.
>После этого я еще немножко верю в то, что суперкрутой спец сможет написать более эффективный код вручную.
Ну если он "лучше знает особенности спаривания команд и устройство конвеера", чем VC, то и сможет, я полагаю.
>Но, во-первых, для этого ему потребуется что-то типа Intel VTune
Вполне возможно, а может, и нет, если он сам "лучше знает особенности спаривания команд и устройство конвеера"
>, а во-вторых, чем эффективнее будет этот код, тем больше риска, что на другой модели процессора он окажется в дупе. А в компиляторе я просто переброшу ключик "Target Processor" и усё.
Вот это единственное серьезное возражение. Но процессоры ежедневно не появляются, а на асме оптимизируют под некий процессор, естественно. Если нужна максимальная призводительность, то при появлении нового процессора, естественно, придется ему пересмотреть код. Кстати, авторам VC — пересмотреть генератор кода. С помощью VTune или своей головы только
>Кстати, джиту еще лучше — ему не надо оптимизировать под "blend".
Ему не надо. Авторам JIT надо
S>А наивные попытки улучшить код, основанные на прочитанной 10 лет назад книжке "Справочное руководство по Intel 80386" способны только просадить эффективность.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, GlebZ, Вы писали:
GZ>>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>>Ты меня не понял. Я и не говорил, что rows будут. Я говорил, что Substring будет. GZ>>А я говорил, что никаких временных объектов при этом создаваться не будет.
PD>Объясни, пожалуйста , тогда еще раз. Вот твой код
PD>int i, l=0; PD>while ((i=myString.IndexOf(",", l))!=-1) PD>{ PD> rows.Add(myString.Substring(i, l-i); PD> l=i; PD>}
PD>Здесь при вызове rows.Add ей передается строка, так ? Эта строка должна быть создана , так ? Путем вызова Substring. И все это делается в цикле. PD>Я согласен с тем, что ссылка на эту строку не хранится у тебя, и сразу после Add на эту временную строку ссылок больше нет и она есть мусор для GC, но ведь она все же создается ? Или нет ?
Создается, но она не мусор. Строка в Net — immutable. Строка не может быть изменена, можно создавать только новые. Row возьмет ссылку на строку и будет оперировать именно им. Он спокойно может сохранять у себя ссылку на строку, передавать ее другим, не боясь что кто-то изменит ее.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Не важно это: все равно проектировать заново придется, даже при использовании готовых "блоков" (модулей, компонент и т.п.).
Только придется проектировать исходя из возможностей компонент, и знания о нем как о черном ящике.
Здравствуйте, raskin,
>> *почему* до сих пор нет ни одного компилятора использующего это эффективно?
R>Современное управление кешем неполно (у кеша всегда есть и своё мнение R>кроме prefetchnta) и непереносимо, поэтому никто не заморачивается?
Вот именно. ИМХО для этого по-хорошему нужен JIT компилятор, а такое есть пока только под .NET, можно сказать. В зачаточном состоянии, скорее всего. Обычные компиляторы сколько лет до ума доводили.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, Sinclair, Вы писали:
S>Видел своими глазами пример. Как знаток x86 — ассемблера страшно раскритиковал код, сгенерированный VC 7.1 в релизе для приведенного мной фрагмента. Действительно, его код был красивше. А студийный — тихий ужос. Какие-то лишние джампы, как-то там регистры странно использовались...
Из интереса решил посмотреть на этот тихий ужас. VC 7.1 , Release
; 9 : int a[100000];
; 10 : for(int i = 0; i < 100000; i++)
Что-то я никакого тихого ужаса не вижу. Код, понятный даже тем, кто "прочитал 10 лет назад книжке "Справочное руководство по Intel 80386" и ничего сверх этого руководства для его понимания не требуется.
Хотя и не scasd. Надо будет написать со scasd и сравнить. Увы, через полчаса лекция.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, Sinclair, Вы писали:
S>>Видел своими глазами пример. Как знаток x86 — ассемблера страшно раскритиковал код, сгенерированный VC 7.1 в релизе для приведенного мной фрагмента. Действительно, его код был красивше. А студийный — тихий ужос. Какие-то лишние джампы, как-то там регистры странно использовались...
PD>Из интереса решил посмотреть на этот тихий ужас. VC 7.1 , Release
Попробовал сейчас в VC7. Да, он действительно генерирует несколько своеобразный код, который быстрее примерно в 2 раза. Впрочм, тихого ужаса все равно нет.
; 19 : {
; 20 :
; 21 : for(int i = 0; i < 100000; i++)
xor eax, eax
$L19464:
; 22 : if(a[i] == 666)
cmp DWORD PTR _a$[esp+eax*4+400012], esi
je SHORT $L19461
cmp DWORD PTR _a$[esp+eax*4+400016], esi
je SHORT $L19461
cmp DWORD PTR _a$[esp+eax*4+400020], esi
je SHORT $L19461
cmp DWORD PTR _a$[esp+eax*4+400024], esi
je SHORT $L19461
cmp DWORD PTR _a$[esp+eax*4+400028], esi
je SHORT $L19461
add eax, 5
cmp eax, 100000 ; 000186a0H
jl SHORT $L19464
$L19461:
Проверил я и вариант с repne scasd. Точно, медленнее.
Так что я должен с тобой согласиться — неплохо они компилятор сделали. Жаль, у меня Intel compiler не установлен, любопытно было бы сравнить.
Здравствуйте, GlebZ, Вы писали:
GZ>Создается, но она не мусор. Строка в Net — immutable. Строка не может быть изменена, можно создавать только новые. Row возьмет ссылку на строку и будет оперировать именно им. Он спокойно может сохранять у себя ссылку на строку, передавать ее другим, не боясь что кто-то изменит ее.
Это все верно, но тут ты играешь на том, что эти подстроки нужны и дальше (в данном случае будут использованы в rows). А вот когда они дальше как таковые не нужны (т.е нигде храниться не будут) — тут-то ты и проиграешь.
p=strtok(myString,",");
while(p)
{
printf("%d\n",p);
p = strtok(NULL,",")
}
Здесь эти временные подстроки никому не нужны после печати. Если ты это перепишешь на С#, то получится
int i, l=0;
while ((i=myString.IndexOf(",", l))!=-1)
{
Console.WriteLine(myString.Substring(i, l-i);
l=i;
}
В итоге для того, чтобы распечатать подстроки, ты создал их, напечатал, и, конечно, они стали добычей GC. А я их не создавал. Правда, строку испортил, но это уж так strtok сделана. Могу свою функцию написать, которая то же сделает, но строку портить не будет.
Если считешь мой пример искусственным — хорошо, пусть в задаче требуется разбить на слова, а потом каждое слово проверить каким-то образом. Я опять-таки подстроки делать не буду, а ты будешь.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Это все верно, но тут ты играешь на том, что эти подстроки нужны и дальше (в данном случае будут использованы в rows).
Ага. Но это очень частый случай.
PD>А вот когда они дальше как таковые не нужны (т.е нигде храниться не будут) — тут-то ты и проиграешь.
В большинстве случаев нужны. Иначе никак кроме подпоркой такую строку не назвать. Но безусловно есть и описанный случай.
string str=str.Substring(1,10).ToUpper(); //здесь действительно есть промежуточная строка.
В случае C++ можно построить без промежуточной строки. Тут можно только упомянуть, что GC с такими промежуточными строками на ура.
PD>p=strtok(myString,","); PD>while(p) PD>{ PD> printf("%d\n",p); PD> p = strtok(NULL,",") PD>}
Да уж. Действительно сильно искуственный пример.
strok — злобная функция, так как она использует static данные на уровне CRT. И соответсвенно, два выполнения strtok — прямой путь к нетрадиционному сексу. Лучше всего вообще забыть о ее существовании.
В большинстве случаев, если обработки строки распределена. и обработка меняет состояние строки, придется делать промежуточные строки. В случае Net — это придется делать и при локальной обработке.
PD>В итоге для того, чтобы распечатать подстроки, ты создал их, напечатал, и, конечно, они стали добычей GC. А я их не создавал. Правда, строку испортил, но это уж так strtok сделана. Могу свою функцию написать, которая то же сделает, но строку портить не будет.
Именно. А если у кого-то была ссылка на строку, то тебе надо делать копии. Но чаще придется делать копии, из-за того что ты не можешь предположить, будет строка изменяться, или нет. Тут эта проблема решена.
PD>Если считешь мой пример искусственным — хорошо, пусть в задаче требуется разбить на слова, а потом каждое слово проверить каким-то образом. Я опять-таки подстроки делать не буду, а ты будешь.
По крайней мере одну придется делать(и не дай бог что TestWord тоже использует strtok):
gear nuke wrote: >> > *почему* до сих пор нет ни одного компилятора использующего это > эффективно? > > R>Современное управление кешем неполно (у кеша всегда есть и своё мнение > R>кроме prefetchnta) и непереносимо, поэтому никто не заморачивается? > > Вот именно. ИМХО для этого по-хорошему нужен JIT компилятор, а такое > есть пока только под .NET, можно сказать. В зачаточном состоянии, скорее > всего. Обычные компиляторы сколько лет до ума доводили.
Зачем JIT? Хотя бы нормальная система команд. Тогда они её друг у друга
лицензируют. Но это должна быть система, позволяющая объяснить
процессору, что эти данные дороже, чем более новые.
Чтобы определить в runtime характеристики кэша и прочего и сгенерировать оптимальный код на лету.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth