Value-типы в Обероне и .NET
От: Oyster Украина https://github.com/devoyster
Дата: 05.08.05 10:53
Оценка: +1
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Про преимущества я ответил в самом начале. Повторяю: Допустим я люблю писать программы на оберонах, но мне хочется заиметь библиотеки написанные под дотнет. Тут у меня есть два пути:


СГ>а) Либо написать компилятор моего любимого оберона под дотнет, но тогда я: 1) потеряю чисто оберонистые библиотеки исходников которых у меня нет; 2) заработаю огромадные потери производительности, так как дотнетская вычислительная модель (или как там это назвать) "слабее" чем оберонистая (в дотнете нет нормальных value-типов, массивов, вложенных процедур).


Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?

08.08.05 12:50: Ветка выделена из темы .NET из НЕТ!
Автор: Cyberax
Дата: 04.08.05
— AndrewVK
Re[8]: Value-типы в Обероне и .NET
От: Курилка Россия http://kirya.narod.ru/
Дата: 05.08.05 11:00
Оценка: :))
Здравствуйте, Oyster, Вы писали:

O>Здравствуйте, Сергей Губанов, Вы писали:


СГ>>Про преимущества я ответил в самом начале. Повторяю: Допустим я люблю писать программы на оберонах, но мне хочется заиметь библиотеки написанные под дотнет. Тут у меня есть два пути:


СГ>>а) Либо написать компилятор моего любимого оберона под дотнет, но тогда я: 1) потеряю чисто оберонистые библиотеки исходников которых у меня нет; 2) заработаю огромадные потери производительности, так как дотнетская вычислительная модель (или как там это назвать) "слабее" чем оберонистая (в дотнете нет нормальных value-типов, массивов, вложенных процедур).


O>Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?


+ в чём "громадные потери" и кто их мерял и как? Звучит как очередное голословное утверждение, которые уже сильно утомляют. Или потери за счёт оверхэда?
Re[9]: Value-типы в Обероне и .NET
От: Oyster Украина https://github.com/devoyster
Дата: 05.08.05 11:03
Оценка: :))) :)))
Здравствуйте, Курилка, Вы писали:

O>>Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?


К>+ в чём "громадные потери" и кто их мерял и как? Звучит как очередное голословное утверждение, которые уже сильно утомляют. Или потери за счёт оверхэда?


Сводки с полей: "Лютый оверхэд напрочь снёс урожай value-типов".

PS: злые мы тут... аргументированно злые
Re[8]: Value-типы в Обероне и .NET
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 05.08.05 11:16
Оценка:
Здравствуйте, Oyster, Вы писали:

O>Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?


Про value-типы:
здесь Programming in Oberon
Niklaus Wirth
Part 4
23. Object-oriented Programming
23.4. Handlers and Messages
(Вопрос уже затрагивался здесь
Автор: Сергей Губанов
Дата: 06.07.05
)

Про value-type массивы:
http://www.rsdn.ru/Forum/Message.aspx?mid=1258083&only=1
Автор: Сергей Губанов
Дата: 06.07.05

http://www.rsdn.ru/Forum/Message.aspx?mid=1056192&only=1
Автор: Сергей Губанов
Дата: 04.03.05


Про вложенные процедуры:
Поскольку из вложенной процедуры видны локальные переменные процедуры-агрегата (принцип локальности), то с помощью них можно легко быстро и эффективно сделать то, что без них требует определения нового класса или структуры. Посмотрите исходники, поймете.
Re[9]: Value-типы в Обероне и .NET
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 05.08.05 11:33
Оценка:
Здравствуйте, Курилка, Вы писали:

К>+ в чём "громадные потери" и кто их мерял и как? Звучит как очередное голословное утверждение, которые уже сильно утомляют. Или потери за счёт оверхэда?


При реализации Gardens Point Component Pascal for .NET, так навскидку, что вспомнил:
1) контекст вложенных процедур пришлось реализовывать через скрытые структуры.
2) value-type массивы эмулировать ссылочными со скрытым копированием и автоматическим созданием
3) Наследование от value-type типов пришлось эмулировать параллельным созданием двух типов struct и class для соответствующих записей для всей иерархии наследования

и многое другое...

короче появились скрытые от программиста накладные расходы на ровном месте.

На счет громадных потерь в отсутсвие value-type массивов я уже где-то тут показывал код который работает в шесть раз медленнее из-за того что при каждой активации процедуры создает новый массив. Идея в следующем:
void Calculate()
{
  byte[] tmpArray = new byte[N];
  ....
  ....
  ....
}

Calculate() вызывается во многих потоках и исполняются параллельно и асинхронно, так что созданные tmpArray всегда скатываются в последнее (третье) поколение GC, так что GC тормозит по страшному в моем примере работа замедлялась в 6 раз по сравнению со случаем когда все tmpArray создавались заранее раз и навсегда (отнимая лишнюю память и засоряя дизайн класса). Если бы были value-type массивы, то можно было бы создавать их на стеке:
PROCEDURE Calculate;
  VAR tmpArray: ARRAY N OF BYTE;
BEGIN
  ...
  ...
  ...
END Calculate;
Re[9]: Value-типы в Обероне и .NET
От: Oyster Украина https://github.com/devoyster
Дата: 05.08.05 11:42
Оценка: 1 (1) +1
Здравствуйте, Сергей Губанов, Вы писали:

O>>Какие претензии к value-типам и массивам в .NET? И чего такого могут вложенные паскалевские процедуры — в чём их преимущество?


СГ>Про value-типы:

СГ>здесь Programming in Oberon
СГ>Niklaus Wirth
СГ>Part 4
СГ>23. Object-oriented Programming
СГ>23.4. Handlers and Messages
СГ>(Вопрос уже затрагивался здесь
Автор: Сергей Губанов
Дата: 06.07.05
)


И причём тут value-типы, позвольте спросить? Как я понял, в топике был затронут вопрос приведения object к заданному reference-типу. Дык ведь в .NET есть is и as — кто мешает их использовать? Чем они хуже?

СГ>Про value-type массивы:

СГ>http://www.rsdn.ru/Forum/Message.aspx?mid=1258083&only=1
Автор: Сергей Губанов
Дата: 06.07.05

СГ>http://www.rsdn.ru/Forum/Message.aspx?mid=1056192&only=1
Автор: Сергей Губанов
Дата: 04.03.05


Опять же — никакой потери перфоманса из-за размещения массивов в managed heap, а не на стеке, не вижу в упор. Ведь массив value-типов в памяти будет "по старинке" храниться — все значения подряд. Где тут жуткая потеря производительности?

Вы в тех сообщениях писали о том, что надо много мелких объектов размещать и мол массив на стеке этому очень поможет. А почему бы не использовать пул, как вам посоветовали? Имхо размещать в стеке полметра данных — это перебор. Ошибка проектирования.

СГ>Про вложенные процедуры:

СГ>Поскольку из вложенной процедуры видны локальные переменные процедуры-агрегата (принцип локальности), то с помощью них можно легко быстро и эффективно сделать то, что без них требует определения нового класса или структуры. Посмотрите исходники, поймете.

Ну это-то ясно. Но вы о производительности говорили — разве не так? Производительность тут не падает ни разу.


В общем, вы уж извините, но опять наблюдаю то же самое, что и раньше в ваших ответах, — какую-то нечеловеческую упёртость, слепую веру в Оберон и неприятие чужих аргументов. Иногда даже складывается впечатление, что вы специально выкапываете в ответах коллег непринципиальные мелочи, чтобы потом гордо развернуть флаг Оберона над головой...

И напоследок скажу — как видите, ничуть .NET не тормознутей в приведённых вами случаях.
Re[10]: Value-типы в Обероне и .NET
От: Oyster Украина https://github.com/devoyster
Дата: 05.08.05 11:46
Оценка: +2
Здравствуйте, Сергей Губанов, Вы писали:

СГ>На счет громадных потерь в отсутсвие value-type массивов я уже где-то тут показывал код который работает в шесть раз медленнее из-за того что при каждой активации процедуры создает новый массив. Идея в следующем:


[... code skipped ...]

СГ>Calculate() вызывается во многих потоках и исполняются параллельно и асинхронно, так что созданные tmpArray всегда скатываются в последнее (третье) поколение GC, так что GC тормозит по страшному в моем примере работа замедлялась в 6 раз по сравнению со случаем когда все tmpArray создавались заранее раз и навсегда (отнимая лишнюю память и засоряя дизайн класса). Если бы были value-type массивы, то можно было бы создавать их на стеке:


[... code skipped ...]

Как я вам уже писал (Re[9]: .NET из НЕТ!
Автор: Oyster
Дата: 05.08.05
), в такой ситуации стек — это не решение проблемы. Потому как слишком много туда класть нехорошо (я о ситуациях, когда вызовов параллельных много) — это вам всяк скажет. Синхронный пул массивов мне видится более приемлимой альтернативой (опять же, более эффективно можно будет реюзать память — висит себе массив в пуле и выделять его не надо даже из стека).
Re[11]: Value-типы в Обероне и .NET
От: Курилка Россия http://kirya.narod.ru/
Дата: 05.08.05 11:50
Оценка:
Здравствуйте, Oyster, Вы писали:

...

O>Как я вам уже писал (Re[9]: .NET из НЕТ!
Автор: Oyster
Дата: 05.08.05
), в такой ситуации стек — это не решение проблемы. Потому как слишком много туда класть нехорошо (я о ситуациях, когда вызовов параллельных много) — это вам всяк скажет. Синхронный пул массивов мне видится более приемлимой альтернативой (опять же, более эффективно можно будет реюзать память — висит себе массив в пуле и выделять его не надо даже из стека).


Создаётся ощущение, что Сергей борется с ветряными мельницами и пытается найти преимущество оберона в программах криво написанных с архитектурной т.зр.
Re[11]: Value-типы в Обероне и .NET
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 05.08.05 16:51
Оценка:
Здравствуйте, Oyster, Вы писали:

O>Как я вам уже писал (Re[9]: .NET из НЕТ!
Автор: Oyster
Дата: 05.08.05
), в такой ситуации стек — это не решение проблемы. Потому как слишком много туда класть нехорошо (я о ситуациях, когда вызовов параллельных много) — это вам всяк скажет. Синхронный пул массивов мне видится более приемлимой альтернативой (опять же, более эффективно можно будет реюзать память — висит себе массив в пуле и выделять его не надо даже из стека).


А если не надо слишком много класть?
Мне в том случае надо было, например, 100 чисел и как быть? Создавать массив:
void Calculate()
{
  int[] tmp = new int[100];
  ...
}

при каждой активации процедуры напрягая почем зря GC или еще круче — завести синхронный пул массивов?


А может, ну его этот дотнет, и по простому на стеке:
PROCEDURE Calculate;
  VAR tmp: ARRAY 100 OF INTEGER;
BEGIN
  ...
END Calculate;
Re[12]: Value-типы в Обероне и .NET
От: Oyster Украина https://github.com/devoyster
Дата: 06.08.05 06:18
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

O>>Как я вам уже писал (Re[9]: .NET из НЕТ!
Автор: Oyster
Дата: 05.08.05
), в такой ситуации стек — это не решение проблемы. Потому как слишком много туда класть нехорошо (я о ситуациях, когда вызовов параллельных много) — это вам всяк скажет. Синхронный пул массивов мне видится более приемлимой альтернативой (опять же, более эффективно можно будет реюзать память — висит себе массив в пуле и выделять его не надо даже из стека).


СГ>А если не надо слишком много класть?

СГ>Мне в том случае надо было, например, 100 чисел и как быть? Создавать массив:
СГ>
СГ>void Calculate()
СГ>{
СГ>  int[] tmp = new int[100];
СГ>  ...
СГ>}
СГ>

СГ>при каждой активации процедуры напрягая почем зря GC или еще круче — завести синхронный пул массивов?

Почему нет? У вас бы 100 интов, кстати, выделились довольно быстро, — managed heap работает быстрее, чем, например, С++-heap. Да и пул тут — самое оно. Естественно, не имеет смысла заводить в пуле 1000 массивов по 100 int — лучше включить мозг и завести 10 по 10'000 int (к примеру). Такой подход (большие массивы) может быть и поэффективнее, чем ваша идея со стеком.

И, Оберон всемогущий, почему вы так уверены, что массив на стеке спасёт всю вашу производительность? Давайте меряться — приводите работающие законченные примеры.
Re[12]: Value-типы в Обероне и .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 06.08.05 08:54
Оценка: +2
Здравствуйте, Сергей Губанов, Вы писали:

СГ>А может, ну его этот дотнет, и по простому на стеке:

СГ>
СГ>PROCEDURE Calculate;
СГ>  VAR tmp: ARRAY 100 OF INTEGER;
СГ>BEGIN
СГ>  ...
СГ>END Calculate;
СГ>


И что будет если вернуть указатель на этот массив из процедуры?

Да и сама задача надумана. Создай класс, размести в нем массив и используй его в коде. Ну, или создай статический массив.

Ну, и не так уж много занимает создание массива в куче. Это всего тактов на 10 больше чем в стэке. Это может повлиять на производительность только если процедура вызывается в огромном фикле.
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: Value-типы в Обероне и .NET
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 06.08.05 15:19
Оценка:
Здравствуйте, 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 еще хужее становится).
Re[13]: Value-типы в Обероне и .NET
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 06.08.05 15:28
Оценка:
Здравствуйте, Oyster, Вы писали:

O> Давайте меряться — приводите работающие законченные примеры.


Я уже приводил, искать ссылку не охота, лучше еще раз приведу, вот:
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 array
            int[] 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.
Re[14]: Value-типы в Обероне и .NET
От: Oyster Украина https://github.com/devoyster
Дата: 06.08.05 15:48
Оценка: +1
Здравствуйте, Сергей Губанов, Вы писали:

O>> Давайте меряться — приводите работающие законченные примеры.


[... skipped ...]

Вообще-то, я имел в виду ненадуманный пример программы на C# и Обероне, причём в программе на Обероне пусть используются мегапреимущества оного (как-то — локальные массивы). Полагаю, C# будет медленнее (ибо managed), но зато C#-вцы смогут показать вам пару правильных решений проблемы "локальных массивов".

Тот код, что привели вы, совершенно не показывает преимуществ обожествляемого вами Оберона, он сравнивает C# с ... правильно, с C#! Причём указывает на совершенно очевидную вещь — не выделять память каждый раз получается быстрее, чем выделять её. Кстати, в случае массива на стеке её всё равно пришлось бы выделять (на стеке), так что пример как минимум некорректен — мы так и не увидели, насколько выделение массива на стеке быстрее оного из managed heap.

Но прекраснее всего то, что ваш же пример показывает эффективность подхода с кешированием массивов вместо создания их каждый раз (т.е. подхода с использованием пула массивов) — вы сами повторно используете один и тот же массив.

Так что в пользе "оберонистых" массивов на стеке не убедили. Зато с полезностью пулов вроде как сами согласились
Re[14]: Value-типы в Обероне и .NET
От: VladD2 Российская Империя www.nemerle.org
Дата: 06.08.05 18:28
Оценка: 2 (2) -2 :))
Здравствуйте, Сергей Губанов, Вы писали:

СГ>А как это сделать?


Тебе вижнее. Что в обероне нельзя вернуть указатель на массив?

СГ>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>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[15]: Value-типы в Обероне и .NET
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 08.08.05 05:55
Оценка: -1
Здравствуйте, 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;
Re[16]: Value-типы в Обероне и .NET
От: Oyster Украина https://github.com/devoyster
Дата: 08.08.05 06:36
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

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 из НЕТ!
Автор: VladD2
Дата: 06.08.05


СГ>3) Более тяжеловесно супротив элементарного локального VAR tmp: ARRAY 3 OF BYTE;


Ну и на этот случай вам предлагали решение, разве не так?


Резюмирую: складывается впечатление, что уважаемый Сергей Губанов совершенно не хочет выслушивать аргументы форумчан. Такое ощущение, что Сергей схватился за свою мегафичу "локальных массивов" и считает, что её отсутствие уже влечёт за собой несостоятельность того или иного языка. Но ведь это не так, ибо другие языки предоставляют другие (часто не менее эффективные) пути решения той же проблемы. К примеру, в Обероне нет дотнетовских атрибутов — что, теперь Оберон сакс? Никто из форумчан себе таких заявлений не позволял (кроме Сергея, который считает, что раз в C# нет локальных массивов, то он несостоятелен, как язык).

Конкретно меня задели эти вот слова Сергея:

а) Либо написать компилятор моего любимого оберона под дотнет, но тогда я: 1) потеряю чисто оберонистые библиотеки исходников которых у меня нет; 2) заработаю огромадные потери производительности, так как дотнетская вычислительная модель (или как там это назвать) "слабее" чем оберонистая (в дотнете нет нормальных value-типов, массивов, вложенных процедур).


Я попытался показать, что никаких "огромадных" потерь производительности не будет, что есть и другие способы работы с временными массивами, кроме создания массивов на стеке... боюсь, я не был услышан. Или же меня просто не хотели слушать.

Сергей, повторю ещё раз, специально для вас, — наличие или отсутствие той или иной фичи в языке ещё не говорит о его превосходстве/низменности. Это может говорить лишь о незнании других фич языка, заменяющих отсутствующую фичу.
Re[13]: Value-типы в Обероне и .NET
От: Павел Кузнецов  
Дата: 08.08.05 06:36
Оценка:
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
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[15]: Value-типы в Обероне и .NET
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 08.08.05 06:37
Оценка:
Здравствуйте, 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 — указатель на массив передается по ссылке (только запись)
Re[16]: Value-типы в Обероне и .NET
От: Oyster Украина https://github.com/devoyster
Дата: 08.08.05 06:50
Оценка: +2
Здравствуйте, Сергей Губанов, Вы писали:

VD>>Да такая растрата памяти. Просто жуть. 400 байт. Дтнетное приложение, правда, при загрузке 7-15 отъедает, но о чем не поговоришь ради искуства. Да? Или у тебя вся программа состоит из часто-содающихся массивов?

VD>>От одного мелкого массива никому плохо не стаен.

СГ>Нет, если бы речь шла о 400 байтах или даже о 20 мегабайтах, я бы и не стал поднимать этот вопрос.

СГ>Речь же идет о всей доступной памяти. Когда несколько потоков активно вызвают процедуру Calculate внутри которой при каждой ее активации создается вспомогательный массив, то дотнетовская система управления памятью предпочитает каждый раз захватывать новую память до тех пор пока не исчерпает всю доступную оперативную память. Только после этого происходит чистка памяти. Кстати, легко сообразить, что практически все эти массивы на момент чистки находятся в последнем поколении GC, то есть их чистка происходит от этого еще медленнее.

Почему это на момент чистки практически все они находятся во 2-м поколении? Да не занесёт их туда — они же используются относительно недолго. Скорее всего, они дальше нулевого не попадут — используются недолго, в freachable queue не попадут...

Эксперты, поправьте если неправ.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.