Здравствуйте, Сергей Губанов, Вы писали:
СГ>Про преимущества я ответил в самом начале. Повторяю: Допустим я люблю писать программы на оберонах, но мне хочется заиметь библиотеки написанные под дотнет. Тут у меня есть два пути:
СГ>а) Либо написать компилятор моего любимого оберона под дотнет, но тогда я: 1) потеряю чисто оберонистые библиотеки исходников которых у меня нет; 2) заработаю огромадные потери производительности, так как дотнетская вычислительная модель (или как там это назвать) "слабее" чем оберонистая (в дотнете нет нормальных value-типов, массивов, вложенных процедур).
Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?
08.08.05 12:50: Ветка выделена из темы .NET из НЕТ!
Здравствуйте, Oyster, Вы писали:
O>Здравствуйте, Сергей Губанов, Вы писали:
СГ>>Про преимущества я ответил в самом начале. Повторяю: Допустим я люблю писать программы на оберонах, но мне хочется заиметь библиотеки написанные под дотнет. Тут у меня есть два пути:
СГ>>а) Либо написать компилятор моего любимого оберона под дотнет, но тогда я: 1) потеряю чисто оберонистые библиотеки исходников которых у меня нет; 2) заработаю огромадные потери производительности, так как дотнетская вычислительная модель (или как там это назвать) "слабее" чем оберонистая (в дотнете нет нормальных value-типов, массивов, вложенных процедур).
O>Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?
+ в чём "громадные потери" и кто их мерял и как? Звучит как очередное голословное утверждение, которые уже сильно утомляют. Или потери за счёт оверхэда?
Здравствуйте, Курилка, Вы писали:
O>>Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?
К>+ в чём "громадные потери" и кто их мерял и как? Звучит как очередное голословное утверждение, которые уже сильно утомляют. Или потери за счёт оверхэда?
Сводки с полей: "Лютый оверхэд напрочь снёс урожай value-типов".
Здравствуйте, Oyster, Вы писали:
O>Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?
Про value-типы: здесь Programming in Oberon
Niklaus Wirth
Part 4
23. Object-oriented Programming
23.4. Handlers and Messages
(Вопрос уже затрагивался здесь
Про вложенные процедуры:
Поскольку из вложенной процедуры видны локальные переменные процедуры-агрегата (принцип локальности), то с помощью них можно легко быстро и эффективно сделать то, что без них требует определения нового класса или структуры. Посмотрите исходники, поймете.
Здравствуйте, Курилка, Вы писали:
К>+ в чём "громадные потери" и кто их мерял и как? Звучит как очередное голословное утверждение, которые уже сильно утомляют. Или потери за счёт оверхэда?
При реализации Gardens Point Component Pascal for .NET, так навскидку, что вспомнил:
1) контекст вложенных процедур пришлось реализовывать через скрытые структуры.
2) value-type массивы эмулировать ссылочными со скрытым копированием и автоматическим созданием
3) Наследование от value-type типов пришлось эмулировать параллельным созданием двух типов struct и class для соответствующих записей для всей иерархии наследования
и многое другое...
короче появились скрытые от программиста накладные расходы на ровном месте.
На счет громадных потерь в отсутсвие value-type массивов я уже где-то тут показывал код который работает в шесть раз медленнее из-за того что при каждой активации процедуры создает новый массив. Идея в следующем:
Calculate() вызывается во многих потоках и исполняются параллельно и асинхронно, так что созданные tmpArray всегда скатываются в последнее (третье) поколение GC, так что GC тормозит по страшному в моем примере работа замедлялась в 6 раз по сравнению со случаем когда все tmpArray создавались заранее раз и навсегда (отнимая лишнюю память и засоряя дизайн класса). Если бы были value-type массивы, то можно было бы создавать их на стеке:
PROCEDURE Calculate;
VAR tmpArray: ARRAY N OF BYTE;
BEGIN
...
...
...
END Calculate;
Здравствуйте, Сергей Губанов, Вы писали:
O>>Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?
СГ>Про value-типы: СГ>здесь Programming in Oberon СГ>Niklaus Wirth СГ>Part 4 СГ>23. Object-oriented Programming СГ>23.4. Handlers and Messages СГ>(Вопрос уже затрагивался здесь
И причём тут value-типы, позвольте спросить? Как я понял, в топике был затронут вопрос приведения object к заданному reference-типу. Дык ведь в .NET есть is и as — кто мешает их использовать? Чем они хуже?
СГ>Про value-type массивы: СГ>http://www.rsdn.ru/Forum/Message.aspx?mid=1258083&only=1
Опять же — никакой потери перфоманса из-за размещения массивов в managed heap, а не на стеке, не вижу в упор. Ведь массив value-типов в памяти будет "по старинке" храниться — все значения подряд. Где тут жуткая потеря производительности?
Вы в тех сообщениях писали о том, что надо много мелких объектов размещать и мол массив на стеке этому очень поможет. А почему бы не использовать пул, как вам посоветовали? Имхо размещать в стеке полметра данных — это перебор. Ошибка проектирования.
СГ>Про вложенные процедуры: СГ>Поскольку из вложенной процедуры видны локальные переменные процедуры-агрегата (принцип локальности), то с помощью них можно легко быстро и эффективно сделать то, что без них требует определения нового класса или структуры. Посмотрите исходники, поймете.
Ну это-то ясно. Но вы о производительности говорили — разве не так? Производительность тут не падает ни разу.
В общем, вы уж извините, но опять наблюдаю то же самое, что и раньше в ваших ответах, — какую-то нечеловеческую упёртость, слепую веру в Оберон и неприятие чужих аргументов. Иногда даже складывается впечатление, что вы специально выкапываете в ответах коллег непринципиальные мелочи, чтобы потом гордо развернуть флаг Оберона над головой...
И напоследок скажу — как видите, ничуть .NET не тормознутей в приведённых вами случаях.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>На счет громадных потерь в отсутсвие value-type массивов я уже где-то тут показывал код который работает в шесть раз медленнее из-за того что при каждой активации процедуры создает новый массив. Идея в следующем:
[... code skipped ...]
СГ>Calculate() вызывается во многих потоках и исполняются параллельно и асинхронно, так что созданные tmpArray всегда скатываются в последнее (третье) поколение GC, так что GC тормозит по страшному в моем примере работа замедлялась в 6 раз по сравнению со случаем когда все tmpArray создавались заранее раз и навсегда (отнимая лишнюю память и засоряя дизайн класса). Если бы были value-type массивы, то можно было бы создавать их на стеке:
), в такой ситуации стек — это не решение проблемы. Потому как слишком много туда класть нехорошо (я о ситуациях, когда вызовов параллельных много) — это вам всяк скажет. Синхронный пул массивов мне видится более приемлимой альтернативой (опять же, более эффективно можно будет реюзать память — висит себе массив в пуле и выделять его не надо даже из стека).
), в такой ситуации стек — это не решение проблемы. Потому как слишком много туда класть нехорошо (я о ситуациях, когда вызовов параллельных много) — это вам всяк скажет. Синхронный пул массивов мне видится более приемлимой альтернативой (опять же, более эффективно можно будет реюзать память — висит себе массив в пуле и выделять его не надо даже из стека).
Создаётся ощущение, что Сергей борется с ветряными мельницами и пытается найти преимущество оберона в программах криво написанных с архитектурной т.зр.
), в такой ситуации стек — это не решение проблемы. Потому как слишком много туда класть нехорошо (я о ситуациях, когда вызовов параллельных много) — это вам всяк скажет. Синхронный пул массивов мне видится более приемлимой альтернативой (опять же, более эффективно можно будет реюзать память — висит себе массив в пуле и выделять его не надо даже из стека).
А если не надо слишком много класть?
Мне в том случае надо было, например, 100 чисел и как быть? Создавать массив:
void Calculate()
{
int[] tmp = new int[100];
...
}
при каждой активации процедуры напрягая почем зря GC или еще круче — завести синхронный пул массивов?
А может, ну его этот дотнет, и по простому на стеке:
PROCEDURE Calculate;
VAR tmp: ARRAY 100 OF INTEGER;
BEGIN
...
END Calculate;
), в такой ситуации стек — это не решение проблемы. Потому как слишком много туда класть нехорошо (я о ситуациях, когда вызовов параллельных много) — это вам всяк скажет. Синхронный пул массивов мне видится более приемлимой альтернативой (опять же, более эффективно можно будет реюзать память — висит себе массив в пуле и выделять его не надо даже из стека).
СГ>А если не надо слишком много класть? СГ>Мне в том случае надо было, например, 100 чисел и как быть? Создавать массив: СГ>
СГ>при каждой активации процедуры напрягая почем зря GC или еще круче — завести синхронный пул массивов?
Почему нет? У вас бы 100 интов, кстати, выделились довольно быстро, — managed heap работает быстрее, чем, например, С++-heap. Да и пул тут — самое оно. Естественно, не имеет смысла заводить в пуле 1000 массивов по 100 int — лучше включить мозг и завести 10 по 10'000 int (к примеру). Такой подход (большие массивы) может быть и поэффективнее, чем ваша идея со стеком.
И, Оберон всемогущий, почему вы так уверены, что массив на стеке спасёт всю вашу производительность? Давайте меряться — приводите работающие законченные примеры.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>А может, ну его этот дотнет, и по простому на стеке: СГ>
СГ>PROCEDURE Calculate;
СГ> VAR tmp: ARRAY 100 OF INTEGER;
СГ>BEGIN
СГ> ...
СГ>END Calculate;
СГ>
И что будет если вернуть указатель на этот массив из процедуры?
Да и сама задача надумана. Создай класс, размести в нем массив и используй его в коде. Ну, или создай статический массив.
Ну, и не так уж много занимает создание массива в куче. Это всего тактов на 10 больше чем в стэке. Это может повлиять на производительность только если процедура вызывается в огромном фикле.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
СГ>>А может, ну его этот дотнет, и по простому на стеке: СГ>>
СГ>>PROCEDURE Calculate;
СГ>> VAR tmp: ARRAY 100 OF INTEGER;
СГ>>BEGIN
СГ>> ...
СГ>>END Calculate;
СГ>>
VD>И что будет если вернуть указатель на этот массив из процедуры?
А как это сделать?
VD>Да и сама задача надумана. Создай класс, размести в нем массив и используй его в коде.
1) Это вместо обычного-то VAR tmp: ARRAY 100 OF INTEGER; мне нужно создать целый класс, а не жирновато ли будет?
2) Массив tmp мне нужен только внутри PROCEDURE Calculate; а все остальное время существования объекта он только память почем зря будет отъедать.
VD>Ну, или создай статический массив.
Нельзя, каждый поток работает со своим собственным экземпляром массива tmp.
VD>Ну, и не так уж много занимает создание массива в куче. Это всего тактов на 10 больше чем в стэке. Это может повлиять на производительность только если процедура вызывается в огромном фикле.
1) И еще в нескольких потоках.
2) И еще когда памяти в системе мало (тогда GC еще хужее становится).
Я уже приводил, искать ссылку не охота, лучше еще раз приведу, вот:
namespace TestLocalArray
{
sealed class Object
{
const int N = 100;
System.Threading.Thread thread;
int count;
// global
// int[] histogram = new int[N];public int Count {get{return this.count;}}
int Calculate()
{
// clear global array
// System.Array.Clear(this.histogram, 0, this.histogram.Length);
// local arrayint[] histogram = new int[N];
for(int i = 0; i < histogram.Length; i++)
{
histogram[i]++;
}
int result = 0;
for(int i = 0; i < histogram.Length; i++)
{
result += histogram[i];
}
return result;
}
void Activity()
{
try
{
while(true)
{
int x = this.Calculate();
this.count++;
System.Threading.Thread.Sleep(0);
}
}
catch(System.Threading.ThreadAbortException)
{
return;
}
}
public Object()
{
this.thread = new System.Threading.Thread(new System.Threading.ThreadStart(this.Activity));
}
public void Start()
{
this.thread.Start();
}
public void Stop()
{
if(this.thread != null)
{
this.thread.Abort(null);
this.thread = null;
}
}
}
class Program
{
conts int M = 500;
Object[] objects = new Object[M];
void Run()
{
System.Console.WriteLine("Program started");
for(int i = 0; i < this.objects.Length; i++) { this.objects[i] = new Object(); }
System.Console.WriteLine("objects created");
for(int i = 0; i < this.objects.Length; i++) { this.objects[i].Start(); }
System.Console.WriteLine("threads running");
System.Console.WriteLine("sleep 15 seconds...");
System.Threading.Thread.Sleep(15000);
for(int i = 0; i < this.objects.Length; i++) { this.objects[i].Stop(); }
System.Console.WriteLine("threads stopped");
long result = 0;
for(int i = 0; i < this.objects.Length; i++)
{
result += this.objects[i].Count;
}
System.Console.WriteLine("result = {0}", result);
System.Console.WriteLine("Program stopped.");
}
static void Main(string[] args)
{
Program p = new Program();
p.Run();
System.Console.ReadLine();
}
}
}
Меняете локальный массив на глобальный (раскомментируя соответствующие строчки) получаете разницу в несколько раз. Увеличиваете N и M — получаете еще большую разницу, т.е зависимость от них нелинейная.
Слова "глобальный" или "локальный" массив здесь применены по отношению к процедуре Calculate.
Здравствуйте, Сергей Губанов, Вы писали:
O>> Давайте меряться — приводите работающие законченные примеры.
[... skipped ...]
Вообще-то, я имел в виду ненадуманный пример программы на C# и Обероне, причём в программе на Обероне пусть используются мегапреимущества оного (как-то — локальные массивы). Полагаю, C# будет медленнее (ибо managed), но зато C#-вцы смогут показать вам пару правильных решений проблемы "локальных массивов".
Тот код, что привели вы, совершенно не показывает преимуществ обожествляемого вами Оберона, он сравнивает C# с ... правильно, с C#! Причём указывает на совершенно очевидную вещь — не выделять память каждый раз получается быстрее, чем выделять её. Кстати, в случае массива на стеке её всё равно пришлось бы выделять (на стеке), так что пример как минимум некорректен — мы так и не увидели, насколько выделение массива на стеке быстрее оного из managed heap.
Но прекраснее всего то, что ваш же пример показывает эффективность подхода с кешированием массивов вместо создания их каждый раз (т.е. подхода с использованием пула массивов) — вы сами повторно используете один и тот же массив.
Так что в пользе "оберонистых" массивов на стеке не убедили. Зато с полезностью пулов вроде как сами согласились
Здравствуйте, Сергей Губанов, Вы писали:
СГ>А как это сделать?
Тебе вижнее. Что в обероне нельзя вернуть указатель на массив?
СГ>1) Это вместо обычного-то VAR tmp: ARRAY 100 OF INTEGER; мне нужно создать целый класс, а не жирновато ли будет?
О, да! Такая куча работы. Ну, да я тебе помогу.
class SomeClass
{
}
В следующий раз найди мое сообщение и скопируй это море кода если самому написать в лом.
СГ>2) Массив tmp мне нужен только внутри PROCEDURE Calculate; а все остальное время существования объекта он только память почем зря будет отъедать.
Да такая растрата памяти. Просто жуть. 400 байт. Дтнетное приложение, правда, при загрузке 7-15 отъедает, но о чем не поговоришь ради искуства. Да? Или у тебя вся программа состоит из часто-содающихся массивов?
VD>>Ну, или создай статический массив.
СГ>Нельзя, каждый поток работает со своим собственным экземпляром массива tmp.
О. Уже потоки. Ну, тогда пометь ее атрибутом [ThreadStatic] или заведи таки класс.
VD>>Ну, и не так уж много занимает создание массива в куче. Это всего тактов на 10 больше чем в стэке. Это может повлиять на производительность только если процедура вызывается в огромном фикле.
СГ>1) И еще в нескольких потоках. СГ>2) И еще когда памяти в системе мало (тогда GC еще хужее становится).
От одного мелкого массива никому плохо не стаен. А вот язык этим самым поганится по полной программе.
Понимашь, ли. Есть такие понятия как непротиворичивость и последовательность. Такие два столпа логики. Так вот C# декларирует, что я могу всегда вернуть ссылку на ссылочный объект. Все массивы ссылочные объекты и соотвественно я всегда могу вернуть ссылку на них.
Причем передача ссылок на массивы значительно эффективнее нежели копирование их значений. Зная эти правила я могу не задумываясь о тонкостях писать эффетивный, простой и, что очень важно, безопасный код. Мне не нужно возиться с указателями или адресами в явном виде. Мне не нужно думать о безопасности. Я думаю о массиве как о первокласной переменой. Для меня массив мало чем отличается от любого другого объекта, да и от примитивных типов.
А теперь зададимся вопросом. Зачем мне какой-то геморой если проблемы проивзодительности связанные с созданием массивов очень редки и довольно просто объходятся? Неужели оно того стит?
Ну, и в конце концов я всегда могу переключиться в небезопасный режим и создавать массивы в стеке. Только вот желания не возникает.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Oyster, Вы писали:
O>Вообще-то, я имел в виду ненадуманный пример программы на C# и Обероне, причём в программе на Обероне пусть используются мегапреимущества оного (как-то — локальные массивы).
А пример у меня не надуманный. Мне реально нужно проводить вычисления использующие вспомогательный массив одновременно в нескольких потоках.
O> Полагаю, C# будет медленнее (ибо managed), но зато C#-вцы смогут показать вам пару правильных решений проблемы "локальных массивов".
1) Оберон, вообще-то, не менее managed чем C# или Java.
2) Ну пусть приведут что-либо аналогичное:
PROCEDURE Calculate;
VAR tmp: ARRAY 3 OF BYTE;
BEGIN
...
END Calculate;
буду только рад. Я на работе как раз на C# пишу, так что дельные советы по нему внимательно выслушиваю...
O>Тот код, что привели вы, совершенно не показывает преимуществ обожествляемого вами Оберона, он сравнивает C# с ... правильно, с C#! Причём указывает на совершенно очевидную вещь — не выделять память каждый раз получается быстрее, чем выделять её. Кстати, в случае массива на стеке её всё равно пришлось бы выделять (на стеке), так что пример как минимум некорректен — мы так и не увидели, насколько выделение массива на стеке быстрее оного из managed heap.
1) Время вызова процедуры не зависит от того сколько места на стеке занимают ее переменные (в разумных пределах естественно). Так что создание/удаление массива (разумного размера) на стеке дается даром.
2) Если бы нужно было только выделение массивов в managed heap то было бы еще хорошо, а надо же еще и их удаление. А для удаления нужен GC. Именно он и создает замедление работы программы.
O>Но прекраснее всего то, что ваш же пример показывает эффективность подхода с кешированием массивов вместо создания их каждый раз (т.е. подхода с использованием пула массивов) — вы сами повторно используете один и тот же массив.
Замечательно. Но зачем мне нужны эти массивы в то время когда процедура Calculate не работает? Зачем эти массивы занимают память всё остальное время?
O>Так что в пользе "оберонистых" массивов на стеке не убедили.
Я привел программу, которая работает в несколько раз медленнее если создает вспомогательные массивы при каждой активации процедуры, и она Вас не убедила. А что же тогда Вас убедило бы?
O>Зато с полезностью пулов вроде как сами согласились
Это вынужденная мера имеющая свои недостатки:
1) Занимает лишную память даже тогда когда ни один экземпляр процедуры Calculate не активен.
2) Засоряет дизайн класса.
3) Более тяжеловесно супротив элементарного локального VAR tmp: ARRAY 3 OF BYTE;
Здравствуйте, Сергей Губанов, Вы писали:
O>>Вообще-то, я имел в виду ненадуманный пример программы на C# и Обероне, причём в программе на Обероне пусть используются мегапреимущества оного (как-то — локальные массивы).
СГ>А пример у меня не надуманный. Мне реально нужно проводить вычисления использующие вспомогательный массив одновременно в нескольких потоках.
Если б он был не надуманным, вы бы применили другой подход в гонке за перфомансом. Тот же пул массивов или worker-класс, метод которого всегда вызывается в одном потоке и который, соответственно, несёт некую информацию о текущем контексте выполнения (как-то: тот же массив, который не надо будет пересоздавать при каждом вызове метода). Кстати, у вас в вашем примере где-то так и сделано — есть worker-класс Object, у него есть массив. Т.е. проблемы нет.
O>> Полагаю, C# будет медленнее (ибо managed), но зато C#-вцы смогут показать вам пару правильных решений проблемы "локальных массивов".
СГ>1) Оберон, вообще-то, не менее managed чем C# или Java.
И таки очень может быть, что по итогу Оберон ещё и медленнее на аналогичных операциях?
СГ>2) Ну пусть приведут что-либо аналогичное: СГ>
СГ>PROCEDURE Calculate;
СГ> VAR tmp: ARRAY 3 OF BYTE;
СГ>BEGIN
СГ> ...
СГ>END Calculate;
СГ>
СГ>буду только рад. Я на работе как раз на C# пишу, так что дельные советы по нему внимательно выслушиваю...
Worker-класс, пул, использование одного dword, трёх byte... по ситуации.
O>>Тот код, что привели вы, совершенно не показывает преимуществ обожествляемого вами Оберона, он сравнивает C# с ... правильно, с C#! Причём указывает на совершенно очевидную вещь — не выделять память каждый раз получается быстрее, чем выделять её. Кстати, в случае массива на стеке её всё равно пришлось бы выделять (на стеке), так что пример как минимум некорректен — мы так и не увидели, насколько выделение массива на стеке быстрее оного из managed heap.
СГ>1) Время вызова процедуры не зависит от того сколько места на стеке занимают ее переменные (в разумных пределах естественно). Так что создание/удаление массива (разумного размера) на стеке дается даром.
Хорошо, пусть будет так.
СГ>2) Если бы нужно было только выделение массивов в managed heap то было бы еще хорошо, а надо же еще и их удаление. А для удаления нужен GC. Именно он и создает замедление работы программы.
Где результаты тестов? Давайте будем серьёзными дядями и тётями, вылезем из песочницы и сравним C# и Оберон, наконец!
O>>Но прекраснее всего то, что ваш же пример показывает эффективность подхода с кешированием массивов вместо создания их каждый раз (т.е. подхода с использованием пула массивов) — вы сами повторно используете один и тот же массив.
СГ>Замечательно. Но зачем мне нужны эти массивы в то время когда процедура Calculate не работает? Зачем эти массивы занимают память всё остальное время?
Какое "всё остальное время"? При грамотной реализации вполне можно сделать пул самоочищающимся. Например, сделать пул на слабых ссылках — GC сам подберёт долгое время не используемые массивы из пула.
O>>Так что в пользе "оберонистых" массивов на стеке не убедили.
СГ>Я привел программу, которая работает в несколько раз медленнее если создает вспомогательные массивы при каждой активации процедуры, и она Вас не убедила. А что же тогда Вас убедило бы?
Это знаете ли, как если бы вы на Обероне эмулировали Хаскелевский поток (или "бесконечный" список или как его там), ибо вам так удобнее. Работая на C#, нужно уметь использовать фичи C#. Я уже даже молчу про пулы. И про то, что сравнения производительности C# и Оберона я так и не увидел. Корректно сравнивать C# с C# только в том случае, если программа, написанная на Обероне, работает в точности так же быстро, как и аналогичная, но написанная на C#, в чём я лично сомневаюсь.
O>>Зато с полезностью пулов вроде как сами согласились
СГ>Это вынужденная мера имеющая свои недостатки: СГ>1) Занимает лишную память даже тогда когда ни один экземпляр процедуры Calculate не активен.
Почему? Пул — это как бы идея, я ничего про реализацию не говорил. Можно, например, создавать массивы в пуле по необходимости и хранить слабые ссылки на них, чтобы GC их убивал, если памяти станет мало.
СГ>2) Засоряет дизайн класса.
Отчего же засоряет? Вот в конкретном данном случае пусть массив из 100 интов на стеке будет красивее. Но представьте себе временные массивы по мегабайту, например... Опять же, VladD2 вам уже писал, почему в C# нет локальных массивов: Re[12]: .NET из НЕТ!
СГ>3) Более тяжеловесно супротив элементарного локального VAR tmp: ARRAY 3 OF BYTE;
Ну и на этот случай вам предлагали решение, разве не так?
Резюмирую: складывается впечатление, что уважаемый Сергей Губанов совершенно не хочет выслушивать аргументы форумчан. Такое ощущение, что Сергей схватился за свою мегафичу "локальных массивов" и считает, что её отсутствие уже влечёт за собой несостоятельность того или иного языка. Но ведь это не так, ибо другие языки предоставляют другие (часто не менее эффективные) пути решения той же проблемы. К примеру, в Обероне нет дотнетовских атрибутов — что, теперь Оберон сакс? Никто из форумчан себе таких заявлений не позволял (кроме Сергея, который считает, что раз в C# нет локальных массивов, то он несостоятелен, как язык).
Конкретно меня задели эти вот слова Сергея:
а) Либо написать компилятор моего любимого оберона под дотнет, но тогда я: 1) потеряю чисто оберонистые библиотеки исходников которых у меня нет; 2) заработаю огромадные потери производительности, так как дотнетская вычислительная модель (или как там это назвать) "слабее" чем оберонистая (в дотнете нет нормальных value-типов, массивов, вложенных процедур).
Я попытался показать, что никаких "огромадных" потерь производительности не будет, что есть и другие способы работы с временными массивами, кроме создания массивов на стеке... боюсь, я не был услышан. Или же меня просто не хотели слушать.
Сергей, повторю ещё раз, специально для вас, — наличие или отсутствие той или иной фичи в языке ещё не говорит о его превосходстве/низменности. Это может говорить лишь о незнании других фич языка, заменяющих отсутствующую фичу.
VladD2,
> И что будет если вернуть указатель на этот массив из процедуры?
А что будет, если вернуть (управляемую) ссылку на локальную переменную? Правильно, плохо будет. Поэтому такой код, если бы были разрешены массивы в стеке, верификацию бы не проходил, как и попытки вернуть (управляемую) ссылку на локальную переменную. В общем, в самих по себе массивах в стеке ничего плохого нет, равно как и в возврате (управляемых) ссылок. Вопрос в их комбинации с другими аспектами, что при желании можно пресекать.
С массивами в CLI/CLR/.Net этом смысле проблема другого свойства. В CLI/CLR/.Net приходится заранее указывать как будут создаваться объекты того или иного типа, указывая к какой разновидности тип относится: reference или value. Сделать массивы value-типом в остальных случаях (возварат значений из функций и т.п.) было бы слишком накладно. Соответственно, сделали reference-типом, что и влечет за собой невозможность создания их в стеке.
В общем, вполне ожидаемое ограничение выбранной объектной модели CLI/CLR/.Net. Безопасность здесь, в общем, ни при чем. Вполне можно было бы доработать, если бы потребовали для всех managed языков поддержку типизированного забоксенного значения. Тогда можно было бы делать так (воображаемый синтаксис на C++/CLI):
array<int>^ f()
{
array<int> a(10); // создали массив в стеке -- в настоящее время невозможно
. . .
return a; // при желании вернули, массив "забоксился"
}
Точно так же, как и для int (реальный синтаксис на C++/CLI):
int^ f()
{
int i = 10; // создали целое в стеке
. . .
return i; // вернули, оно "забоксилось"
}
Впрочем, наверное, для VB.Net/C# это слишком сложно...
> Ну, и не так уж много занимает создание массива в куче. Это всего тактов на 10 больше чем в стэке. Это может повлиять на производительность только если процедура вызывается в огромном фикле.
Гм... Правильно ли я понимаю, что ты говоришь, что, скажем, для "маленьких векторов" (a la геометрический вектор), в плане быстродействия фактически не будет разницы в следующих случаях (в т.ч. и с учетом последующей уборки созданных во втором случае объектов сборщиком мусора):
struct V
{
int x;
int y;
int z;
}
. . .
V v = new V();
int[] a = new int[3];
?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, VladD2, Вы писали:
СГ>>А как это сделать?
VD>Тебе вижнее. Что в обероне нельзя вернуть указатель на массив?
Если массив размещен на стеке — нельзя.
Это касается не только массивов.
Вообще любой объект может быть размещен либо в динамической памяти, и тогда доступ к нему осуществляется через указатель; либо на стеке или внутри другого объекта, но тогда доступ к нему осуществляется только по имени и получить указатель на него невозможно — просто нету в языке средства под названием "получение указателя на объект".
СГ>>1) Это вместо обычного-то VAR tmp: ARRAY 100 OF INTEGER; мне нужно создать целый класс, а не жирновато ли будет?
VD>О, да! Такая куча работы. Ну, да я тебе помогу. VD>
class SomeClass
{
}
Не понял юмора.
Вот код:
PROCEDURE P;
VAR a: ARRAY 3 OF BYTE;
BEGIN
...
END P;
Что Вы предлагаете взамен?
VD>Да такая растрата памяти. Просто жуть. 400 байт. Дтнетное приложение, правда, при загрузке 7-15 отъедает, но о чем не поговоришь ради искуства. Да? Или у тебя вся программа состоит из часто-содающихся массивов? VD>От одного мелкого массива никому плохо не стаен.
Нет, если бы речь шла о 400 байтах или даже о 20 мегабайтах, я бы и не стал поднимать этот вопрос.
Речь же идет о всей доступной памяти. Когда несколько потоков активно вызвают процедуру Calculate внутри которой при каждой ее активации создается вспомогательный массив, то дотнетовская система управления памятью предпочитает каждый раз захватывать новую память до тех пор пока не исчерпает всю доступную оперативную память. Только после этого происходит чистка памяти. Кстати, легко сообразить, что практически все эти массивы на момент чистки находятся в последнем поколении GC, то есть их чистка происходит от этого еще медленнее.
VD>Понимашь, ли. Есть такие понятия как непротиворичивость и последовательность. Такие два столпа логики. Так вот C# декларирует, что я могу всегда вернуть ссылку на ссылочный объект. Все массивы ссылочные объекты и соотвественно я всегда могу вернуть ссылку на них. VD>Причем передача ссылок на массивы значительно эффективнее нежели копирование их значений. Зная эти правила я могу не задумываясь о тонкостях писать эффетивный, простой и, что очень важно, безопасный код. Мне не нужно возиться с указателями или адресами в явном виде. Мне не нужно думать о безопасности. Я думаю о массиве как о первокласной переменой. Для меня массив мало чем отличается от любого другого объекта, да и от примитивных типов. VD>А теперь зададимся вопросом. Зачем мне какой-то геморой если проблемы проивзодительности связанные с созданием массивов очень редки и довольно просто объходятся? Неужели оно того стит? VD>Ну, и в конце концов я всегда могу переключиться в небезопасный режим и создавать массивы в стеке. Только вот желания не возникает.
Все эти слова конечно хорошо, но не понятно какое это имеет отношение к value-типам.
Код использующий value-типы не менее безопасен.
А массив это, вообще-то, value-тип, и лишь только в дотнете или яве он лишен этой привилегии.
PROCEDURE Calculate(a: ARRAY OF BYTE; IN b: ARRAY OF BYTE; VAR c: ARRAY OF BYTE; OUT d: ARRAY OF BYTE);
a — массив передается по значению (копируется)
b — массив передается по ссылке (только чтение)
c — массив передается по ссылке (чтение/запись)
d — массив передается по ссылке (только запись)
PROCEDURE Calculate(pa: POINTER TO ARRAY OF BYTE;
VAR pc: POINTER TO ARRAY OF BYTE; OUT pd: POINTER TO ARRAY OF BYTE);
pa — указатель на массив передается по значению (копируется)
(IN pb: POINTER .. — нету такого варианта, константный указатель это уже перебор)
pc — указатель на массив передается по ссылке (чтение/запись)
pd — указатель на массив передается по ссылке (только запись)
Здравствуйте, Сергей Губанов, Вы писали:
VD>>Да такая растрата памяти. Просто жуть. 400 байт. Дтнетное приложение, правда, при загрузке 7-15 отъедает, но о чем не поговоришь ради искуства. Да? Или у тебя вся программа состоит из часто-содающихся массивов? VD>>От одного мелкого массива никому плохо не стаен.
СГ>Нет, если бы речь шла о 400 байтах или даже о 20 мегабайтах, я бы и не стал поднимать этот вопрос. СГ>Речь же идет о всей доступной памяти. Когда несколько потоков активно вызвают процедуру Calculate внутри которой при каждой ее активации создается вспомогательный массив, то дотнетовская система управления памятью предпочитает каждый раз захватывать новую память до тех пор пока не исчерпает всю доступную оперативную память. Только после этого происходит чистка памяти. Кстати, легко сообразить, что практически все эти массивы на момент чистки находятся в последнем поколении GC, то есть их чистка происходит от этого еще медленнее.
Почему это на момент чистки практически все они находятся во 2-м поколении? Да не занесёт их туда — они же используются относительно недолго. Скорее всего, они дальше нулевого не попадут — используются недолго, в freachable queue не попадут...
Здравствуйте, Oyster, Вы писали:
O>Если б он был не надуманным, вы бы применили другой подход в гонке за перфомансом.
Так вот мне и пришлось применить...
O>И таки очень может быть, что по итогу Оберон ещё и медленнее на аналогичных операциях?
Не понял. Что именно?
O> При грамотной реализации вполне можно сделать пул самоочищающимся. Например, сделать пул на слабых ссылках — GC сам подберёт долгое время не используемые массивы из пула.
Да, не спорю, можно. Но и Вы тоже не спорьте с тем, что всё это слишком сложно по сравнению с тривиальнейшим размещением временного массива на стеке.
O> Такое ощущение, что Сергей схватился за свою мегафичу "локальных массивов" и считает, что её отсутствие уже влечёт за собой несостоятельность того или иного языка.
Мелко берёте. Я кидал камень не в огород языка C#, а в огород двух платформ: дотнета и джавы.
O>
O>а) Либо написать компилятор моего любимого оберона под дотнет, но тогда я: 1) потеряю чисто оберонистые библиотеки исходников которых у меня нет; 2) заработаю огромадные потери производительности, так как дотнетская вычислительная модель (или как там это назвать) "слабее" чем оберонистая (в дотнете нет нормальных value-типов, массивов, вложенных процедур).
O>Я попытался показать, что никаких "огромадных" потерь производительности не будет, что есть и другие способы работы с временными массивами, кроме создания массивов на стеке... боюсь, я не был услышан. Или же меня просто не хотели слушать.
Если я напишу программу с процедурой:
PROCEDURE P;
VAR a: ARRAY N OF INTEGER;
BEGIN
...
END P;
то будучи скомпилированной и запущенной под родной оберон системой времени исполнения она будет работать быстро; но она же будучи скомпилирована под платформу дотнет или под платформу джава будет работать в несколько раз медленнее из-за того что строчка VAR a: ARRAY N OF INTEGER; будет скрытым образом переделана в строчку:
int[] a = new int[N];
Вот они — скрытые, неявные, не ожидаемые огромные (в несколько раз) потери!!!!! Вот именно это я и имел в виду. Писать компиляторы оберонов под платформы дотнет и джава значит заранее соглашаться с тем, что программы скомпилированные такими компиляторами будут работать медленнее (ибо разные вычислительные модели у этих платформ). А вот наоборот, исполнять дотнетские или джавовские программы на соответствующим образом "подкрученной" обероннистой системе времени исполнения, не приведет к потере производительности дотнетских или джава программ. Вот именно в этом смысле я заявил, что дотнетская (и джавовская) вычислительная модель "слабее" чем оберонистая (надо было сказать не "слабее", а "меньше", т.е. оберонистая платформа может вобрать в себя дотнетскую и ждавистую, а наоборот нет). Да, действительно, слово "слабее" неудачное...
O>Сергей, повторю ещё раз, специально для вас, — наличие или отсутствие той или иной фичи в языке ещё не говорит о его превосходстве/низменности. Это может говорить лишь о незнании других фич языка, заменяющих отсутствующую фичу.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Здравствуйте, Oyster, Вы писали:
O>>Если б он был не надуманным, вы бы применили другой подход в гонке за перфомансом.
СГ>Так вот мне и пришлось применить...
Ну и? Не вижу в этом ничего плохого.
O>>И таки очень может быть, что по итогу Оберон ещё и медленнее на аналогичных операциях?
СГ>Не понял. Что именно?
Это я спрашивал — быстрее ли Оберон, чем C#, или нет?
O>> При грамотной реализации вполне можно сделать пул самоочищающимся. Например, сделать пул на слабых ссылках — GC сам подберёт долгое время не используемые массивы из пула.
СГ>Да, не спорю, можно. Но и Вы тоже не спорьте с тем, что всё это слишком сложно по сравнению с тривиальнейшим размещением временного массива на стеке.
O>> Такое ощущение, что Сергей схватился за свою мегафичу "локальных массивов" и считает, что её отсутствие уже влечёт за собой несостоятельность того или иного языка.
СГ>Мелко берёте. Я кидал камень не в огород языка C#, а в огород двух платформ: дотнета и джавы.
Цитирую себя:
... несостоятельность того или иного языка ...
Я специально абстрагировался от конкретного языка тут. Вы вообще обычно кидаете камень куда-то далеко — сразу и не разберёшь, в кого
O>>
O>>а) Либо написать компилятор моего любимого оберона под дотнет, но тогда я: 1) потеряю чисто оберонистые библиотеки исходников которых у меня нет; 2) заработаю огромадные потери производительности, так как дотнетская вычислительная модель (или как там это назвать) "слабее" чем оберонистая (в дотнете нет нормальных value-типов, массивов, вложенных процедур).
O>>Я попытался показать, что никаких "огромадных" потерь производительности не будет, что есть и другие способы работы с временными массивами, кроме создания массивов на стеке... боюсь, я не был услышан. Или же меня просто не хотели слушать.
СГ>Если я напишу программу с процедурой: СГ>
СГ>PROCEDURE P;
СГ> VAR a: ARRAY N OF INTEGER;
СГ>BEGIN
СГ> ...
СГ>END P;
СГ>
СГ>то будучи скомпилированной и запущенной под родной оберон системой времени исполнения она будет работать быстро; но она же будучи скомпилирована под платформу дотнет или под платформу джава будет работать в несколько раз медленнее из-за того что строчка VAR a: ARRAY N OF INTEGER; будет скрытым образом переделана в строчку: СГ>
СГ>int[] a = new int[N];
СГ>
СГ>Вот они — скрытые, неявные, не ожидаемые огромные (в несколько раз) потери!!!!! Вот именно это я и имел в виду. Писать компиляторы оберонов под платформы дотнет и джава значит заранее соглашаться с тем, что программы скомпилированные такими компиляторами будут работать медленнее (ибо разные вычислительные модели у этих платформ).
Тут, милейший, всё зависит от производителей компиляторов.
СГ>А вот наоборот, исполнять дотнетские или джавовские программы на соответствующим образом "подкрученной" обероннистой системе времени исполнения, не приведет к потере производительности дотнетских или джава программ.
Почему это?
СГ>Вот именно в этом смысле я заявил, что дотнетская (и джавовская) вычислительная модель "слабее" чем оберонистая (надо было сказать не "слабее", а "меньше", т.е. оберонистая платформа может вобрать в себя дотнетскую и ждавистую, а наоборот нет). Да, действительно, слово "слабее" неудачное...
О Боги! Чувствую, затягивает меня булькающая пучина Оберона...
А если серьёзно, то чего-то не видно, как Оберон рулит. А если ещё серьёзней, то на великую технологий Оберон не тянет, вы уж извините. Как, кстати, Оберон собирается вбирать те же дотнетовские reflection и атрибуты?
А если совсем серьёзно, то тогда уж Лисп, но никак не Оберон.
O>>Сергей, повторю ещё раз, специально для вас, — наличие или отсутствие той или иной фичи в языке ещё не говорит о его превосходстве/низменности. Это может говорить лишь о незнании других фич языка, заменяющих отсутствующую фичу.
СГ>А для платформы?
Аналогично. Незнание фич, специфичных для платформы, не освобождает от ответственности думать, прежде чем отвечать.
Здравствуйте, Oyster, Вы писали:
O>Почему это на момент чистки практически все они находятся во 2-м поколении?
Есть много потоков.
Несколько из них вызвали Calculate и сидят в ней, вычисляют...
Затем еще несколько вызвали Calculate (каждая из которых вызвала new) в то время когда первые еще не закончили свою работу.
Если Сборщик мусора запускался (поводом для запуска могли быть вызовы new), то он перевел те первые несколько массивов в следующее поколение.
Затем еще несколько потоков залезли в Calculate в то время когда предыдущие потоки еще сидят в ней же.
Если Сборщик мусора запускался, то он перевел уже созданные массивы еще на одно поколение "вглубь".
Если общее количество потоков велико, то так продолжается много раз.
Вдруг, те самые первые несколько потоков наконец-то закончили вычисления и вышли из своих Calculate.
Сколько раз вызывался сборщик мусора за это время? Он вызвался много раз. При каждом его вызове каждый из уже
созданных массивов перемещался в следующее поколение. Так что если потоков много, то с большой вероятностью
на момент удаления каждый из массивов находится в последнем поколении.
Здравствуйте, Сергей Губанов, Вы писали:
O>>Почему это на момент чистки практически все они находятся во 2-м поколении?
СГ>Есть много потоков. СГ>Несколько из них вызвали Calculate и сидят в ней, вычисляют... СГ>Затем еще несколько вызвали Calculate (каждая из которых вызвала new) в то время когда первые еще не закончили свою работу. СГ>Если Сборщик мусора запускался (поводом для запуска могли быть вызовы new), то он перевел те первые несколько массивов в следующее поколение. СГ>Затем еще несколько потоков залезли в Calculate в то время когда предыдущие потоки еще сидят в ней же. СГ>Если Сборщик мусора запускался, то он перевел уже созданные массивы еще на одно поколение "вглубь". СГ>Если общее количество потоков велико, то так продолжается много раз. СГ>Вдруг, те самые первые несколько потоков наконец-то закончили вычисления и вышли из своих Calculate. СГ>Сколько раз вызывался сборщик мусора за это время? Он вызвался много раз. При каждом его вызове каждый из уже СГ>созданных массивов перемещался в следующее поколение. Так что если потоков много, то с большой вероятностью СГ>на момент удаления каждый из массивов находится в последнем поколении.
Ну если у вас Calculate так редко вызывается, то и проблема исчерпывается, т.к. GC будет собирать эти массивы очень редко...
Здравствуйте, Oyster, Вы писали:
O>Ну если у вас Calculate так редко вызывается, то и проблема исчерпывается, т.к. GC будет собирать эти массивы очень редко...
1) А что тогда разница в скорости в несколько раз...
2) А нафига программа то и дело захватывает всю доступную оперативную память (а потом освобождает и так туда-суда, туда-суда) вместо того чтобы работать с фиксированным малым количеством памяти...
СГ>Тут я возился со сборщиками мусора в BlackBox и MS .NET
А ответы в теме читали?... Я это к тому, что там у людей тоже была некая аргументация... или вы и те сообщения успешно игнорировали?
СГ>Если сами надумаете тестировать, то вот один из самых лучших (бесплатных) компиляторов Modula-2/Oberon-2: СГ>http://www.excelsior-usa.com/xds.html
Здравствуйте, Сергей Губанов, Вы писали:
O>>Ну если у вас Calculate так редко вызывается, то и проблема исчерпывается, т.к. GC будет собирать эти массивы очень редко...
СГ>1) А что тогда разница в скорости в несколько раз...
А про это уже пальцы устали отвечать. Читаем выше. Ключевые слова: кеширование массивов.
СГ>2) А нафига программа то и дело захватывает всю доступную оперативную память (а потом освобождает и так туда-суда, туда-суда) вместо того чтобы работать с фиксированным малым количеством памяти...
У меня дежавю — такое ощущение, что я вам уже отвечал на такие вопросы... Отвечу ещё раз — есть и другие подходы к созданию/использованию временных массивов (см. предыдущие сообщения).
Здравствуйте, Oyster, Вы писали:
O>А ответы в теме читали?... Я это к тому, что там у людей тоже была некая аргументация... или вы и те сообщения успешно игнорировали?
Читал.
(Специально отвечаю, чтобы не быть обвиненным в игнорировании этого сообщения . Блин может мне надо после каждого сообщения писать свое: "Ознакомился")
Здравствуйте, Oyster, Вы писали:
O>А про это уже пальцы устали отвечать. Читаем выше. Ключевые слова: кеширование массивов.
O>У меня дежавю — такое ощущение, что я вам уже отвечал на такие вопросы... Отвечу ещё раз — есть и другие подходы к созданию/использованию временных массивов (см. предыдущие сообщения).
Ну значит, Вы наверное уже точно поняли, что вместо того чтобы писать компиляторы: Oberon ---> .Net, JVM, более перспективно "подкручивать" oberon runtime system так чтобы они сами умели исполнять .Net/JVM модули. А то понимаетели "другие подходы" в них...
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Здравствуйте, Oyster, Вы писали:
O>>А ответы в теме читали?... Я это к тому, что там у людей тоже была некая аргументация... или вы и те сообщения успешно игнорировали?
СГ>Читал.
Я это к тому, что из тех сообщений (аргументов, контрагрументов) становится понятно, что нельзя однозначно сказать, что BlackBox GC лучше или быстрее, чем .NET GC.
СГ> СГ>(Специально отвечаю, чтобы не быть обвиненным в игнорировании этого сообщения . Блин может мне надо после каждого сообщения писать свое: "Ознакомился")
Здравствуйте, Сергей Губанов, Вы писали:
O>>А про это уже пальцы устали отвечать. Читаем выше. Ключевые слова: кеширование массивов.
O>>У меня дежавю — такое ощущение, что я вам уже отвечал на такие вопросы... Отвечу ещё раз — есть и другие подходы к созданию/использованию временных массивов (см. предыдущие сообщения).
СГ>Ну значит, Вы наверное уже точно поняли, что вместо того чтобы писать компиляторы: Oberon ---> .Net, JVM, более перспективно "подкручивать" oberon runtime system так чтобы они сами умели исполнять .Net/JVM модули. А то понимаетели "другие подходы" в них...
Нет, не понял. Вы так и не пояснили, как планируется использовать .NET assemblies из-под Оберона. Подъёмный ли это труд? Один reflection чего стоит...
Здравствуйте, Сергей Губанов, Вы писали: СГ>Ну значит, Вы наверное уже точно поняли, что вместо того чтобы писать компиляторы: Oberon ---> .Net, JVM, более перспективно "подкручивать" oberon runtime system так чтобы они сами умели исполнять .Net/JVM модули. А то понимаетели "другие подходы" в них...
Т.е. получается — сами написать толком не можем — стырим-ка библиотеки у МС и иже с ними. Всёже .нет это прежде всего библиотеки входящие во фреймворк + архитектура на которой они построены (непонятно, правда, как использовать библиотеки обходя стороной эту архитектуру )
Здравствуйте, Oyster, Вы писали:
O>Я это к тому, что из тех сообщений (аргументов, контрагрументов) становится понятно, что нельзя однозначно сказать, что BlackBox GC лучше или быстрее, чем .NET GC.
Зато можно сказать при каких условиях BlackBox GC быстрее MS. Net GC в два-три раза и больше (или во всяком случае точно не медленее):
1) Если необходимое количество памяти не превышает объема установленной оперативной памяти.
2) Если нет большого числа (несколько миллионов) фоновых (т.е. никогда не удаляемых) объектов.
Нормальные условия в общем-то.
А временно не нужные фоновые объекты в этом случае просто рекомендуют сохранять на диск...
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Здравствуйте, Oyster, Вы писали:
O>>Я это к тому, что из тех сообщений (аргументов, контрагрументов) становится понятно, что нельзя однозначно сказать, что BlackBox GC лучше или быстрее, чем .NET GC.
СГ>Зато можно сказать при каких условиях BlackBox GC быстрее MS. Net GC в два-три раза и больше (или во всяком случае точно не медленее): СГ>1) Если необходимое количество памяти не превышает объема установленной оперативной памяти. СГ>2) Если нет большого числа (несколько миллионов) фоновых (т.е. никогда не удаляемых) объектов. СГ>Нормальные условия в общем-то.
СГ>А временно не нужные фоновые объекты в этом случае просто рекомендуют сохранять на диск...
Сергей, сколько можно кормить людей данными высосанными из пальца? Конкретные тесты с описанием и итоговыми результатами есть? Нет — значит всё это лишь досужие домыслы, не более
Здравствуйте, Oyster, Вы писали:
O>Нет, не понял. Вы так и не пояснили, как планируется использовать .NET assemblies из-под Оберона. Подъёмный ли это труд? Один reflection чего стоит...
Я сам еще не знаю, мне эта идея в голову недавно пришла.
Но, с другой стороны, есть же пример реализации дотнета под линукс (Mono), работает же.
Значит принципиальных проблем создания своей реализации дотнетского рантайма нет.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>А что будет, если вернуть (управляемую) ссылку на локальную переменную?
Это невозможно. Так что ничего не буедет.
ПК>С массивами в CLI/CLR/.Net этом смысле проблема другого свойства. В CLI/CLR/.Net приходится заранее указывать как будут создаваться объекты того или иного типа, указывая к какой разновидности тип относится: reference или value. Сделать массивы value-типом в остальных случаях (возварат значений из функций и т.п.) было бы слишком накладно. Соответственно, сделали reference-типом, что и влечет за собой невозможность создания их в стеке.
+1
ПК>В общем, вполне ожидаемое ограничение выбранной объектной модели CLI/CLR/.Net. Безопасность здесь, в общем, ни при чем. Вполне можно было бы доработать, если бы потребовали для всех managed языков поддержку типизированного забоксенного значения. Тогда можно было бы делать так (воображаемый синтаксис на C++/CLI): ПК>
ПК>array<int>^ f()
ПК>{
ПК> array<int> a(10); // создали массив в стеке -- в настоящее время невозможно
ПК> . . .
ПК> return a; // при желании вернули, массив "забоксился"
ПК>}
ПК>
Ну, и ужасен этот синтаксис.
А вообще-тогда и боксить ничего не нужно. Можно просто копию возврщать. Но тогда пользователю прийдется думать о том где размещается массив и к каким последствиям это приводит. Ускорения реального это все равно не принесло бы, а вот геморроя принесло бы и не мало.
К тому же еще пришлось бы всегда контролировать объемы данных занимаемых в стеке, так как совершенно реальным стало бы переполнение стека. На сегодня в стеке больших объектов быть не может по определнию.
ПК>Точно так же, как и для int (реальный синтаксис на C++/CLI): ПК>
ПК>int^ f()
ПК>{
ПК> int i = 10; // создали целое в стеке
ПК> . . .
ПК> return i; // вернули, оно "забоксилось"
ПК>}
ПК>
Для int такое можно написать только из любви к искуству . Так
int f()
{
int i = 10; // создали целое в стеке
. . .
return i;
}
как-то проще.
ПК>Впрочем, наверное, для VB.Net/C# это слишком сложно...
Это просто неопревданно сложно. Это как раз тот мусор о котором в С++ приходится думать вместо того чтобы думать о решении прикладной задачи. C# явно проектировали как интуитивно-понятный язык на слежение за которым не нужно тратить времени. Подобные извраты как раз напрочь учивают эту концепцию, а значит и смысл языка.
ПК>Гм... Правильно ли я понимаю, что ты говоришь, что, скажем, для "маленьких векторов" (a la геометрический вектор), в плане быстродействия фактически не будет разницы в следующих случаях (в т.ч. и с учетом последующей уборки созданных во втором случае объектов сборщиком мусора): ПК>
ПК>struct V
ПК>{
ПК> int x;
ПК> int y;
ПК> int z;
ПК>}
ПК>. . .
ПК>V v = new V();
ПК>
ПК>
ПК>int[] a = new int[3];
ПК>
ПК>?
Разница конечно будет, но уловить ее можно будет только в дийствительно критических местах и при условии, что код их использующий очень мал и быстр.
Простой пример, обычно бывает быстрее пользоваться не IEnumerable<T> или IList<T> для перебора коллекции, а скопировать значение коллекции через ICollection в массив и перебирать уже его содержимое. И происходит это потому, что создание массива довольно быстрая операция, а при переборе через IEnumerable<T> каждый вызов несет в себе дополнительне телодвижения связанные с продвижением указателя, контролем неизменяемости коллекции и т.п.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Если массив размещен на стеке — нельзя. СГ>Это касается не только массивов. СГ>Вообще любой объект может быть размещен либо в динамической памяти, и тогда доступ к нему осуществляется через указатель; либо на стеке или внутри другого объекта, но тогда доступ к нему осуществляется только по имени и получить указатель на него невозможно — просто нету в языке средства под названием "получение указателя на объект".
Ну, и что делать если объект размещен в стеке, а его нужно вернуть клиенту?
Или по другому. А что делать если массив нужно передать подпроцедуре?
СГ>Не понял юмора. СГ>Вот код: СГ>
СГ>PROCEDURE P;
СГ> VAR a: ARRAY 3 OF BYTE;
СГ>BEGIN
СГ> ...
СГ>END P;
СГ>
СГ>Что Вы предлагаете взамен?
class P
{
private byte[] _a = new byte[3];
public void F()
{
...
}
}
Ну, и далее:
P p = new P();
for (int i = 0; i < 1000; i++)
p.F();
СГ>Нет, если бы речь шла о 400 байтах или даже о 20 мегабайтах, я бы и не стал поднимать этот вопрос. СГ>Речь же идет о всей доступной памяти. Когда несколько потоков активно вызвают процедуру Calculate внутри которой при каждой ее активации ...
Тебе же сказали: создай массивы заранее. Будет как раз 400 байт на всю программу.
СГ>Все эти слова конечно хорошо, но не понятно какое это имеет отношение к value-типам. СГ>Код использующий value-типы не менее безопасен.
value-типы еще долны быть эффективны. Ну, и к тому же по концепции дотнета value-типы — это именно класс типов. Так что один и тот же тип не может быть одновременно и value-типом, и ссылочным типом. А введение двух типов массивов уже было бы серьезным усложнением.
СГ>А массив это, вообще-то, value-тип, и лишь только в дотнете или яве он лишен этой привилегии.
С какого это рожна массив — это value-тип? Где такой закон можно найти?
Да, примеры на Паскале приводить не нужно. У него работа с массивами сделана крайне не удобно и не эффективно. В С/С++ массивы вообще небезопасны и не удобны.
ЗЫ
В общем, единственное что мне приходит в голову — это то что джит/пре-джит мог бы распозновать случаи локального использования массивов и размещать память под них в стеке. Но это именно оптимизация рантайма ничего не имеющая общего с языковыми концепциями.
С точки же зрения концепции массивы несоменно очень удачно представляются ссылочными типами. Ими легко манимулировать. А это самое важное. Скорость же со временем все равно дойдет до почти идеальной. С С++ было точно так же. Когда-то его ругали за медленность относитально С, а теперь уже почти все признают, что разницы нет. Даже на С++ проще писать более быстрый код.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Oyster, Вы писали:
СГ>>Кстати, легко сообразить, что практически все эти массивы на момент чистки находятся в последнем поколении GC, то есть их чистка происходит от этого еще медленнее.
O>Почему это на момент чистки практически все они находятся во 2-м поколении? Да не занесёт их туда — они же используются относительно недолго. Скорее всего, они дальше нулевого не попадут — используются недолго, в freachable queue не попадут...
O>Эксперты, поправьте если неправ.
Все соврешенно верно. Пчти все временные объекты подбираются при сборке нулевого поколения.
Как раз проталкивание в первое и второе происходит исключительно с объектами на которые есть ссылки из тех самых поколений или из корней GC. А на временные объекты по определению таких ссылок быть не может.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Сергей Губанов, Вы писали:
O>>Нет, не понял. Вы так и не пояснили, как планируется использовать .NET assemblies из-под Оберона. Подъёмный ли это труд? Один reflection чего стоит...
СГ>Я сам еще не знаю, мне эта идея в голову недавно пришла. СГ>Но, с другой стороны, есть же пример реализации дотнета под линукс (Mono), работает же. СГ>Значит принципиальных проблем создания своей реализации дотнетского рантайма нет.
СГ>Значит всё это только дело времени
Боюсь, ничего хорошего из этой идеи не выйдет (ворчу).
Кстати, а зачем вам вообще какая-то интеграция с .NET? Разве BlackBox не самодостаточен? Потому как если хочется заюзать преимущества .NET во всей полноте, то уж лучше имхо и использовать язык, спроектированный специально для .NET — IL... тьфу, т.е. C#
Здравствуйте, Oyster, Вы писали:
O>Кстати, а зачем вам вообще какая-то интеграция с .NET? Разве BlackBox не самодостаточен?
Вопрос в библиотеках. Сам все не напишешь. Сейчас, под Windows, BlackBox умеет работать с обычными flat-win32-dll и с COM. Если верить маркетологам из MS, то в будущем flat-win32-dll или COM будет появляться все меньше и меньше, а .Net-dll все больше и больше. Отсюда вывод напрашивается сам собой. Что ежели все-таки MS маркетологи окажутся правы, то надо будет "научить" BlackBox понимать еще и дотнетовские dll-ки.
O>Потому как если хочется заюзать преимущества .NET во всей полноте, то уж лучше имхо и использовать язык, спроектированный специально для .NET — IL... тьфу, т.е. C#
Хочется заюзать только библиотеки, но свои программы писать не под .Net, а под более "широкую вычислительную модель".
Здравствуйте, Сергей Губанов, Вы писали:
O>>Кстати, а зачем вам вообще какая-то интеграция с .NET? Разве BlackBox не самодостаточен?
СГ>Вопрос в библиотеках. Сам все не напишешь. Сейчас, под Windows, BlackBox умеет работать с обычными flat-win32-dll и с COM. Если верить маркетологам из MS, то в будущем flat-win32-dll или COM будет появляться все меньше и меньше, а .Net-dll все больше и больше. Отсюда вывод напрашивается сам собой. Что ежели все-таки MS маркетологи окажутся правы, то надо будет "научить" BlackBox понимать еще и дотнетовские dll-ки.
O>>Потому как если хочется заюзать преимущества .NET во всей полноте, то уж лучше имхо и использовать язык, спроектированный специально для .NET — IL... тьфу, т.е. C#
СГ>Хочется заюзать только библиотеки, но свои программы писать не под .Net, а под более "широкую вычислительную модель".
Тяжело будет это сделать... да и перфоманс вряд ли удастся удержать на приемлимом уровне. Ну да это дело уже хозяйское — тут дискутировать не о чем
Здравствуйте, VladD2, Вы писали:
VD>Ну, и что делать если объект размещен в стеке, а его нужно вернуть клиенту?
Это невозможно.
Не надо было размещать такой объект в стеке, а надо было размещать его в динамической памяти.
VD>Или по другому. А что делать если массив нужно передать подпроцедуре?
Это очень просто:
PROCEDURE Clear (VAR a: ARRAY OF INTEGER);
VAR i: INTEGER;
BEGIN FOR i := 0 TO LEN(a) - 1 DO a[i] := 0 END
END Clear
PROCEDURE Proc;
VAR a: ARRAY 3 OF INTEGER;
b: ARRAY N OF INTEGER;
p: POINTER TO ARRAY OF INTEGER;
BEGIN
Clear(a);
Clear(b);
NEW(p, LEN(a)+LEN(b));
Clear(p);
...
END Proc;
VD>value-типы еще долны быть эффективны. Ну, и к тому же по концепции дотнета value-типы — это именно класс типов. Так что один и тот же тип не может быть одновременно и value-типом, и ссылочным типом. А введение двух типов массивов уже было бы серьезным усложнением.
VD> С какого это рожна массив — это value-тип? Где такой закон можно найти?
Усугублю ситуацию. Более того, все типы есть value-типы. А так называемые "ссылочные типы" образуются посредством указателей:
TYPE
A = ARRAY 8 OF BYTE;
P = POINTER TO A;
TYPE
R = RECORD x, y: REAL END;
P = POINTER TO R;
Закон — ну не знаю, есть ли тут законы...
VD>Да, примеры на Паскале приводить не нужно. У него работа с массивами сделана крайне не удобно и не эффективно.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Читал.
-------------------
Поле чудес...
Якубович привычно спрашивает у играющей в этот раз милой блондинки:
— От чего будете страховаться?
— Ой, а можно "угадала все буквы, но не смогла прочесть слово"?
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Oyster, Вы писали:
O>Я это к тому, что из тех сообщений (аргументов, контрагрументов) становится понятно, что нельзя однозначно сказать, что BlackBox GC лучше или быстрее, чем .NET GC.
Вообще-то там было однозначно доказано, что в рельных условиях BlackBox вообще слабо применим, так как при больших объемах наступае колапс в следсвии отсуствия поколений в GC.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Здравствуйте, Oyster, Вы писали:
O>>Ну если у вас Calculate так редко вызывается, то и проблема исчерпывается, т.к. GC будет собирать эти массивы очень редко...
СГ>1) А что тогда разница в скорости в несколько раз... СГ>2) А нафига программа то и дело захватывает всю доступную оперативную память (а потом освобождает и так туда-суда, туда-суда) вместо того чтобы работать с фиксированным малым количеством памяти...
А это тебя нужно спросить. Думаю, что Calculate вызывается наоборот слишком часто. Так что ты просто говоришь не то что есть на самом деле.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Сергей Губанов, Вы писали:
VD>А можно вопрос? Спасибо.
VD>Как в Обероне отсортировать локальны массив?
VD>Ну, объявили мы массив весь из себя стэковый. И вдруг раз и захотелось его отсортировать. Что делать будем?
Я же приводил пример с Clear. С Sort будет аналогично.
MODULE Sorter;
PROCEDURE SortArrayOfInteger* (VAR a: ARRAY OF INTEGER);
BEGIN
...
END SortArrayOfInteger;
PROCEDURE SortArrayOfReal* (VAR a: ARRAY OF REAL);
BEGIN
...
END SortArrayOfReal;
END Sorter.
MODULE MyModule;
IMPORT Sorter;
PROCEDURE MyProcedure;
VAR a: ARRAY 80 OF INTEGER;
BEGIN
...
Sorter.SortArrayOfInteger(a); (* отсортировали *)
...
END MyProcedure;
END MyModule.
СГ>Есть много потоков. СГ>Несколько из них вызвали Calculate и сидят в ней, вычисляют...
Я был не прав.
Сейчас попробовал узнать поколения с помощью System.GC.GetGeneration(), оказалось они в нулевом сидят.
Если внутрь процедуры Calculate в середине вставить Sleep(0) — дескать чего-то мощно вычисляет, то тогда примерно половина массивов падает из нулевого в первое поколение.
До второго поколения никто не добирается. Прикольно. Я-то думал тормоза из-за глубоких поколений. Ан нет, тормоза с нулевым поколением. Да-а-а, представляю какие тормоза начались бы если б те массивы на самом деле удалось бы загнать во второе поколение....
В моем рассуждении ошибка, видимо, была в том, что не так уж долго они сидят внутри Calculate, чтобы успеть совсем состариться...
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Это невозможно. СГ>Не надо было размещать такой объект в стеке, а надо было размещать его в динамической памяти.
Ну, и нафиг это нужно? Других проблем не хватает что ли?
СГ>Усугублю ситуацию. Более того, все типы есть value-типы. А так называемые "ссылочные типы" образуются посредством указателей
Это одна идиалогия. Но тогда вообще не нужно рассуждать о вэлью-типах. В дотнете используется другая. И в ней тип определяет его семантику. Как видишь на дотнете и Яве кода пишется не соизмеримо больше чем на Обероне. Значи народ такая политика более чем устраивает. Дальше делай выводы.
СГ>Меряться будем?
Все давно померяно.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
На самом деле задача с value-типами не такая надуманная как кажется. Вот реальный пример — расчетная сеть (С++):
struct Node // узел
{
double x,y,z; // координаты и прочая попса
Triangle *first; // указатель на первый треугольник вокруг узла
};
struct Triangle
{
Node *nodes[3]; // в треугольнике три узла
Triangle *next[3]; // три списка на братьев по узлуint node_idx(Node *)
{
return// номер узла в массиве nodes
}
Triangle* next_in_node( Node *n )
{
return next[node_idx(n)];
}
};
struct Mesh
{
NodeList nodes;
TriangleList triangles;
};
Смотрим в классцы и видим два агрегированых массива в классе Triangle, и оверхед равен нулю
В C# пришлось бы создавать массивы динамически, и получить удар по перфомансу и расходу памяти.
Чесно говоря спор оберонщика с дот-нетовцами по поводу вычислительных задач вызывает только смех т.к. ни то ни другое для вычислений и CAD/CAM/CAE не удобно. Здесь С++ всех рвет на части и ему нет никакой альтернативы.
Вот пример кода который без гемороя и оверхеда ни на обероне ни на шарпе не сделать
// точка в N-мерном пространствеtemplate <int N, class T>
struct Point;
// точка в 2-мерном пространствеtemplate <class T>
struct Point<2,T>
{
T x, y;
// операторы + - и т.д. поскипаны для краткости
};
// точка в 3-мерном пространствеtemplate <class T>
struct Point<3,T>
{
T x, y, z;
};
// Теперь имея классы Point<2,T>, Point<3,T> и Point<сколько_надо,T>
// наслаждаемся всю оставшуюся жизнь:
// вычисление точки на отрезке в N-мерном пр-веtemplate <int N, class T>
Point<N,T> line_point( const Point<N,T> &A, const Point<N,T> &B, T u )
{
return A + (B-A)*u;
}
// И таким макаром без лишней суеты и напряга
// имеем все остальные алгоритмы и классы
// например сплайн на плоскости и в пространствеtemplate <int N, class T>
struct NurbsCurve
{
typedef Point<N,T> PointNT;
int n;
int p;
vector<T> U; // кнот-вектор
vector<PointNT> P; // опорные точки
vector<T> W; // веса
PointNT point( T u ); // понеслась
};
typedef NurbsCurve<2,double> NurbsCurve2d; // сплайн на плоскостиtypedef NurbsCurve<3,double> NurbsCurve3d; // сплайн в пространстве
При этом в коде я по прежнему имею безгеморойный доступ к компонентам точек x,y,z,w и т.п.
Ни в обероне ни в Шарпе так красиво, просто, быстро и без гемороя с оверхедом не напишешь. Либо копи-пасте для всех алгоритмов и классов на плоскости и в пространстве. Либо прийдется делать вот такого уродца, который ни на что не годится:
struct Point
{
public double[] coords;
public Point( int N )
{
coords = new double[N]; // Позор и отстой
}
}
VladD2,
> ПК>А что будет, если вернуть (управляемую) ссылку на локальную переменную? > > Это невозможно. Так что ничего не буедет.
И с массивами можно было бы запретить. Поэтому возможность вернуть адрес массива агрументом для запрета размещения его в стеке не является.
> ПК>В общем, вполне ожидаемое ограничение выбранной объектной модели CLI/CLR/.Net. Безопасность здесь, в общем, ни при чем. Вполне можно было бы доработать, если бы потребовали для всех managed языков поддержку типизированного забоксенного значения. <...>
> А вообще-тогда и боксить ничего не нужно. Можно просто копию возврщать.
Можно. Но это может оказаться дорогим удовольствием.
> Но тогда пользователю прийдется думать о том где размещается массив и к каким последствиям это приводит.
Бедный пользователь (раньше их программистами называли) -- ему думать для написания программы придется
> Ускорения реального это все равно не принесло бы, а вот геморроя принесло бы и не мало.
Зависит от задачи. Если речь идет об интенсивном использовании маленьких массивов, то размещение их в стеке могло бы существенно снизить нагрузку на сборщик мусора, так же как это делают value-типы. Без возможности создавать массивы в стеке или возможности создавать поля-массивы фиксированного размера эффективная обобщенная реализация тех же геометрических примитивов становится вполне нетривиальной задачей.
> К тому же еще пришлось бы всегда контролировать объемы данных занимаемых в стеке, так как совершенно реальным стало бы переполнение стека. На сегодня в стеке больших объектов быть не может по определнию.
Ну не по определению, а по фактическому использованию. Ничто не мешает сделать структуру с ковырнадцатью полями.
> Простой пример, обычно бывает быстрее пользоваться не IEnumerable<T> или IList<T> для перебора коллекции, а скопировать значение коллекции через ICollection в массив и перебирать уже его содержимое. И происходит это потому, что создание массива довольно быстрая операция, а при переборе через IEnumerable<T> каждый вызов несет в себе дополнительне телодвижения связанные с продвижением указателя, контролем неизменяемости коллекции и т.п.
Еще одно аналогичное неэффективное решение, характерное для данной платформы
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Зависит от задачи. Если речь идет об интенсивном использовании маленьких массивов, то размещение их в стеке могло бы существенно снизить нагрузку на сборщик мусора, так же как это делают value-типы. Без возможности создавать массивы в стеке или возможности создавать поля-массивы фиксированного размера эффективная обобщенная реализация тех же геометрических примитивов становится вполне нетривиальной задачей.
Если что никогда не поздно воспользоваться ансэйвом. Хотя пока что похоже и без этого все ОК. В том же менеджед-директ-иксе сделали обертку на МС++ и все работает на шарпе без проблем.
>> К тому же еще пришлось бы всегда контролировать объемы данных занимаемых в стеке, так как совершенно реальным стало бы переполнение стека. На сегодня в стеке больших объектов быть не может по определнию.
ПК>Ну не по определению, а по фактическому использованию. Ничто не мешает сделать структуру с ковырнадцатью полями.
Задолбашся вручную структуры рисовать. А вот с массивами и строками размещаемыми по месту это получится за просто. На С++ я проблемы со стеком встречал периодически. Причем они были не предсказуемыми. Мы делали КОМ-объекты, а они иногда запускаются черти где и черти-как. А вот в дотнете если получил переполнение стэка — это 100%-но зациклившаяся рекурсия.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Kluev, Вы писали:
K>Вот пример кода который без гемороя и оверхеда ни на обероне ни на шарпе не сделать K>
// точка в 2-мерном пространствеtemplate <class T>
struct Point<2,T>
{
T x, y;
// операторы + - и т.д. поскипаны для краткости
};
// точка в 3-мерном пространствеtemplate <class T>
struct Point<3,T>
{
T x, y, z;
// операторы + - и т.д. поскипаны для краткости
};
Счастье было бы более полным если бы еще не надо было делать специализацию шаблонов отдельно для случаев 2D и 3D, не так ли?
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Здравствуйте, Kluev, Вы писали:
K>>Вот пример кода который без гемороя и оверхеда ни на обероне ни на шарпе не сделать K>> СГ>
СГ>// точка в 2-мерном пространстве
СГ>template <class T>
СГ> struct Point<2,T>
СГ>{
СГ> T x, y;
СГ>// операторы + - и т.д. поскипаны для краткости
СГ>};
СГ>// точка в 3-мерном пространстве
СГ>template <class T>
СГ> struct Point<3,T>
СГ>{
СГ> T x, y, z;
СГ>// операторы + - и т.д. поскипаны для краткости
СГ>};
СГ>
СГ>Счастье было бы более полным если бы еще не надо было делать специализацию шаблонов отдельно для случаев 2D и 3D, не так ли?
Нет. Ты не прав. Для класса Point специализация как раз нужна. Т.к. это все-таки разные классы. В одном (x,y), в другом (x,y,z), а в третьем (x,y,z,w). И в коде писать приятнее point.x, а не point.get_x(), или point.coords[0].
А вот для других классов специализацию для 2d и 3d делать уже не нужно. В том то вся и прелесть. Например NurbsCurve<N,T>, BoundBox<N,T>, и другие прекрасно живут без специализации:
Point<N,T> NurbsCurve<N,T>::point( T u ) const
{
HPoint<N,T> cw;
int p = m_U.p();
int i = m_U.knot_span(u);
T N[Geom::nurbs_p_max + 1];
m_U.calc_basis_funs( N, u, i );
for ( int j = 0; j <= p; j++ )
cw += m_Pw[i - p + j] * N[j];
return cw.euclid();
}
Один и тот же шаблонный код используется для 2D и 3D. Все просто и оверхед на всех уровнях сведен к нулю. Можно сказать, что С++ идеальный язык для задач вычислительной геометрии. Так просто, эффективно и красиво можно сделать на нем. Шарп и оберон здесь отдыхают в далеке.
signature Point =
sig
type t
type p
val + : p* p -> p
val - : p* p -> p
val * : t* p -> p
end;
functor PointOps(Pt:Point) =
struct
open Pt;
fun line_point (a:p,b:p,u:t) = a + u * (b-a);
type NurbsCurve = {U:t list, P:p list, W:t list}
end;
structure Point2D:Point =
struct
type t = real
type p = {x:t,y:t}
val op + = fn ({x=x1,y=y1},{x=x2,y=y2})=> {x=x1+x2,y=y1+y2};
...
end;
structure PointOps2D = PointOps(Point2D);
type NurbsCurve2d = PointOps2D.NurbsCurve;