Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, c-smile, Вы писали:
CS>>Вот такие вот мои мысли в слух если они кому интересны.
PD>Мысли интересны, но не кажется ли тебе, что это можно выразить короче ? А именно — для достаточно часто выполняемых коротких операций не надо дергать кучу , если это возможно и не противоречит высшим идеям При этом неважно, управляемая она или нет. Твой пример можно вполне на неуправляемый С++ перевести, и он там будет так же точно выглядеть, только явный вызов delete добавится. И эффекты будут те же — по скорости.
Вопрос на самом деле лежит в несколько иной плоскости:
Скажем грамотное (или оптимальное) использование memory managers.
Есть ситуации когда микроменеджмент выполняется в том числе с помощью new/delete (malloc/free) и само собой аллокацией на стеке. микроменеджмент памяти это ситуация когда мы знаем точно время жизни объекта.
Не использовать это знание — значит обрекать себя на неоптимальное решение.
И есть ситуации когда GC на высоте — заведомо лучше справляется с ситуацией или позволяет строить более надежную систему.
Имея опыт написания managed/unmanaged компонент на .NET(MC++) и Java(JNI) пришел к заключению
что модель native методов в Java (JNI) лучше. Во многих смыслах.
Macromangement памяти это удел таких систем как Java.
И это хорошо на самом деле что в ней нет всяких подпорок типа using.
Они там не нужны по большому счету.
RAII в С++ — он для этого то что надо. GC в Java — то что нужно.
Это я пытаюсь мыслить в слух в том числе на тему:
Все-в-одном как MC++ например это хорошо или плохо?
Что-то мне говорит что будет как всегда.
Здравствуйте, Sinclair, Вы писали:
CS>>В моем случае это CS>>будет простой набор методов CS>>Graphics.setGradientBrush(...) Это если оно надо. S>А, то есть ты предлагаешь сделать в Graphics по методу на каждый конструктор одного из потомков кисти? Может быть, оно и оправдано. Хотя я не уверен: дело в том, насколько удобно это будет в применении. У тебя не будет возможности в одном месте решить, какой кистью закрашивать, а в другом — какую фигуру нарисовать. Так бы ты сделал метод DrawRectangle, принимающий параметры прямоугольника и кисть, которая взята откуда-то снаружи (из преференсов пользователя, к примеру). И он бы работал для всех кистей, независимо от того, в каком порядке что вызывалось. А при твоем способе нужно следить за тем, чтобы ненароком не испортить состояние Graphics лишним вызовом setXXX(). CS>>Полиморфизм вещь хорошая, но там где он нужен. Все — в меру. Как всегда. S>Верно.
Если действительно нужен полиморфизм здесь то сделать себе поверх этого
class Brush и класс GraphicsEx: public Graphics не представляет проблемы.
Просто это далеко не всегда нужно.
В качесве примера: есть стиль (CSS) у которого в общем случае
четыре border и их brushes, background solid border (+ у меня еще и градиент)
background image brush в разных вариациях и т.д.
Хранить это все безобразие в виде отдельных объектов типа Brush, Color и пр....
Я думаю мысль понятна и продолжать её не надо.
Классовые иерархии вещь конечно хорошая. Если задача в них вмещается. Если же нет — все, труба, они только мешают.
Вот еще пример:
Скажем имея следующий Canvas нужно сделать SVG viewer.
Представим себе что SVG документ загружен в DOM.
Таким образом линейный проход по дереву с простой state machine + эта canvas
это все что нам нужно для rendering. И не нужны там тебе ни Color ни Pen/Brush в виде классов...
Нарисовал — и забыл.
Здравствуйте, McSeem2, Вы писали:
MS>И вот у меня unmanaged объект требует довольно много памяти, а GC об этом ничего не знает и нет никакой возможности ему объяснить, что "этот объект дорогой, его надо подметать ASAP".
Может имеет смысл сделать такие объекты managed?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, n0name2, Вы писали:
N>в Жабе 1.6 будет стековая аллокация — компилятор после того как выполнит все inline посмотрит можно ли все сделать на стеке или нет. т.е.
Боюсь что подобная фича будет представлять собой очередной источник багов, т.к. ожидания программистов и действия компилятора могут не совпадать. На месте разработчиков Java 1.6 я бы держал подобную фичу в большом секрете.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Эффективнее . Вот тебе простой пример
Все равно точнее. Если мы признаемся себе, что точки нам нужны во всех итерациях, то можно вынести их за цикл:
POINT* p = new POINT [3];
for(int i = 0; i < 1000; i++)
{
// и т.д.
}
delete [] p;
Если бы у точки был нетривиальный дефолтный конструктор, то этот код имел бы шанс быть даже эффективнее твоего варианта:
PD>for(int i = 0; i < 1000; i++) PD>{ PD> POINT p[3]; PD> // и т.д. PD>}
PD>1000 раз дергать кучу там, где можно и один раз не дергать — хорошая, кстати иллюстрация на тему микрооптимизации, за которую меня некоторые ругали . Вот таким невиинным кодом убивают производительность. А с точки зрения дизайна здесь вообще ничего нет — дизайн здесь близко не лежит. PD>Конечно, я здесь использую свое знание того, что выделение памяти в стеке произведено будет 1 раз, а не 1000 (кстати, это стандартом ИМХО не определяется, формально ведь память отводится при входе в блок, так ?). Но даже если бы 1000 раз — все равно лучше 1000 раз выполнить что-то вроде add esp,..., чем 1000 раз дергать хип менеджер с его поиском в 2-linked списке.
Я вот не уверен, что надо начинать рассуждения с add esp и прочих туманных деталей. Я всегда старался запоминать наиболее общие утверждения. К тому же как раз плюсы — штука чреватая неявными эффектами. Да, для точки все действительно так, как ты говоришь. Более того, компилятор как раз способен вынести инвариант за цикл (в отличие от случая с new/delete) и получить код, близкий к оптимальному.
PD>Ну не знаю. Никакой проблемы в этом не было, размер стека в любой модели, (кроме tiny, где все вместе в 64К), был ограничен 64 К. Надо было просто _stklen установить. В конце концов никто на запрещщал же в те времена вот такие описания
PD>void f() PD>{ PD>char a[4096] ; PD>//.... PD>}
Да, никто и не запрещал. Вот только моя программа сразу же упала с таким описанием. Я уже не помню, что там надо было установить.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Все равно точнее. Если мы признаемся себе, что точки нам нужны во всех итерациях, то можно вынести их за цикл:
Ну это уже изменение области видимости. А вдруг в объемлющем блоке другой p описан ?
S>Я вот не уверен, что надо начинать рассуждения с add esp и прочих туманных деталей.
Для обучения — сразу не надо. Но все же программист на С++ должен понимать. что там ниже делается. Более того, ИМХО это вообще верно — чтобы качественно программировать на уровне N, надо понимать, что делается на уровне N-1. Уметь самому программировать на уровне N-1 не обязательно.
CS>В данном случае Java выполняет менджмент микро объектов Rect и Brush — создает и удаляет их. Как следствие система жутко не эффективная. Как правило Brush это обертка над системным HBRUSH в конце концов. Т.е. к тому же имеем еще и dispose со всеми вытекающими.
Такая вот мысль в голову пришла. Просьба сильно за нее не бить, если что я не додумал.
А почему бы в языках, работающих в управляемой среде (Java, C#) не разрешить вторичный вызов конструктора ?
class C
{
C(...) {...}
}
C c = new C(1 набор параметров);
// использовали
reconstr (c, C(другой набор параметров));
// естественно, exception, если тип c не есть C)
и т.д.
Конечно, это можно и сейчас сделать, вынеся код в отдельную функцию и вызывая его в первый раз из конструктора, а потом просто так. Но ИМХО это было бы логичнее. Т.е. я пересоздаю объект без перевыделения памяти.
В С++ это, естественно, невозможно, а в управляемой среде ИМХО нет противопоказаний. Ставшие мусором объекты из прежнего экземпляра скушает GC.
Полиморфизма так, конечно, не добьешься, но вот от перевыделения памяти иногда можно избавиться
Brush br = new Brush(Color(0,0,255));
// use blue brush
reconstr (br, Brush(Color(255,0,0));
// use red brush
Pavel Dvorkin,
> А почему бы в языках, работающих в управляемой среде (Java, C#) не разрешить вторичный вызов конструктора ? > > class C > { > C(...) {...} > } > > C c = new C(1 набор параметров); > // использовали > reconstr (c, C(другой набор параметров)); > > // естественно, exception, если тип c не есть C) > > <...> > > В С++ это, естественно, невозможно <...>
Почему?
C* c = new C(1);
. . .
c->~C();
new (c) C(2);
. . .
delete c;
проверку на совпадение типа можно организовать, используя RTTI.
Posted via RSDN NNTP Server 2.0
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, IT, Вы писали:
N>>в Жабе 1.6 будет стековая аллокация — компилятор после того как выполнит все inline посмотрит можно ли все сделать на стеке или нет. т.е.
IT>Боюсь что подобная фича будет представлять собой очередной источник багов, т.к. ожидания программистов и действия компилятора могут не совпадать. На месте разработчиков Java 1.6 я бы держал подобную фичу в большом секрете.
в том то и дело что в строго типизированной и безопасной системе можно безболезненно делать очень радикальные оптимизации в т.ч. на основе статистики собранной в рантайме. это же не C где можно делать арифметические операции над указателями...
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Т.е. я пересоздаю объект без перевыделения памяти.
в Java/C# не нужны ручные оптимизации — все это может делать компилятор или рантайм автоматически. если бы это было действительно узким местом, то давно бы уже так и сделали.
само по себе перевыделение памяти это совершенно бесплатная операция т.к. память не фрагментирована. выделение памяти это просто инкремент одного указателя (даже мутекса нет т.к. у каждого потока свой маленький heap имеется), освобождения как такового тоже нет, просто объект не копируется в следующее поколенье.
единственное что занимает много времени это инициализация объекта (скажем, массивы при выделении инициализируются нулями, ссылки все нулевые и т.д.) но при пересоздании это тоже надо делать.
Здравствуйте, n0name2, Вы писали:
N>в том то и дело что в строго типизированной и безопасной системе можно безболезненно делать очень радикальные оптимизации в т.ч. на основе статистики собранной в рантайме. это же не C где можно делать арифметические операции над указателями...
Только тут надо иметь в виду что стек не резиновый...
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Я имел в виду без вызова деструктора.
Не забывай про финалийзеры. И про то что на объект могут быть ссылки.
Короче такая функциональность приведет к граблям. Пусть уж лучше этим оптимизатор занимается ибо в управляемых средах у него достаточно информации для таких оптимизаций.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Такая вот мысль в голову пришла. Просьба сильно за нее не бить, если что я не додумал.
PD>А почему бы в языках, работающих в управляемой среде (Java, C#) не разрешить вторичный вызов конструктора ?
Я думаю, потому, что это нарушит ссылочную целостность. У нас уже есть ссылки в других объектах на этот объект. И эти другие объекты будут крайне удивлены внезапной смерти того объекта и возникновению нового. У меня сейчас сильнейшее дежа вю, т.к. данная тема уже точно поднималась.
PD>Конечно, это можно и сейчас сделать, вынеся код в отдельную функцию и вызывая его в первый раз из конструктора, а потом просто так. Но ИМХО это было бы логичнее. Т.е. я пересоздаю объект без перевыделения памяти.
PD>Полиморфизма так, конечно, не добьешься, но вот от перевыделения памяти иногда можно избавиться
Выделение памяти в управляемой среде — сверхестественно дешевая операция, сравнимая со стоимостью выделения памяти на стеке в C++. Не там воду ищешь.
Для тех редчайших случаев, когда действительно дешевле переинициализировать объект, чем создавать новый, можно пользоваться двухстадийной инициализацией.
Brush br = new Brush(Color(0,0,255));
br.Init(Color(0,0,255));
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, WolfHound, Вы писали:
WH>Только тут надо иметь в виду что стек не резиновый...
Ага, и то что запросить информацию об объекте и выполнить действия могут через внешний интерфейс типа reflection.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Pavel Dvorkin,
>> А почему бы в языках, работающих в управляемой среде (Java, C#) не разрешить вторичный вызов конструктора ? >> >> <...> >> >> В С++ это, естественно, невозможно <...>
ПК>Почему? ПК>
ПК>C* c = new C(1);
ПК>. . .
c->>~C();
ПК>new (c) C(2);
ПК>. . .
ПК>delete c;
ПК>
Вопрос терминологический, но по-моему, здесь не происходит повторного вызова конструктора
Конструктор чего вызывается повторно? Ведь объект, на который указывает c не существует после вызова соответствующего деструктора
Здравствуйте, WolfHound, Вы писали:
N>>в том то и дело что в строго типизированной и безопасной системе можно безболезненно делать очень радикальные оптимизации в т.ч. на основе статистики собранной в рантайме. это же не C где можно делать арифметические операции над указателями... WH>Только тут надо иметь в виду что стек не резиновый...
в общем да — ну так рантайм знает в точности сколько что занимает и совершенно свободно посчитает сколько туда можно пихать.