Re[5]: Только мне в дотнете не хватает неизменяемого массива
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 31.01.11 09:22
Оценка:
Здравствуйте, samius, Вы писали:

G>>ЗЫ. А кто мешает в качестве низкоуровневневых строительных блоков применять обычные массивы? Главное не отдавать их наружу.

S>То их и мешает использовать, что их надо тщательно прятать.

Но ведь не в этом проблема, а в быстродействии.

Для immutable массивов будут следующие операции и их асимптотика
1)Изменение элемента: O(n)
2)Взятие подмасиива: O(n)
3)Добавление\удаление элемента: O(n)
4)и только взятие элемента по индексу: O(1)

Фактически immutable массив эффективен только в случае доступа на чтение по случайному индексу.
Re[4]: Только мне в дотнете не хватает неизменяемого массива
От: samius Япония http://sams-tricks.blogspot.com
Дата: 31.01.11 09:23
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, samius, Вы писали:


S>>Гораздо интереснее как он будет соотноситься с классом System.Array в терминах ковариантности. Точнее это не сильно интересно, а должно иметь место. Т.е. нужно что бы изменяемый массив можно было подавать в качестве неизменяемого.

S>Не уверен в выделенном. Тогда нельзя будет написать код, полагающийся на иммутабельность переданного ему аргумента.
S>Простейший пример: класс Url может проверять права доступа к переданному адресу в конструкторе, и полагаться на то, что никто его не подменит, благодаря иммутабельности строк. Иначе атакующий сможет подменять адрес после проверки, но до обращения.

Я понял мысль. Но почему бы в таком случае проверяющему не выполнять проверку через GetType()? Хотя тип в рантайме тоже можно подменить unsafe-ом...
Да, надо подумать об этом.
Re[6]: Только мне в дотнете не хватает неизменяемого массива
От: samius Япония http://sams-tricks.blogspot.com
Дата: 31.01.11 09:46
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, samius, Вы писали:


G>>>ЗЫ. А кто мешает в качестве низкоуровневневых строительных блоков применять обычные массивы? Главное не отдавать их наружу.

S>>То их и мешает использовать, что их надо тщательно прятать.

G>Но ведь не в этом проблема, а в быстродействии.

Проблема с быстродействием вытекает из неизменяемости, т.к. изменяемые массивы приходится оборачивать. Это конечно не влияет на алгоритмическую сложность (если не оборачиваем через array.Select(i => i)), но все-таки.

G>Для immutable массивов будут следующие операции и их асимптотика

G>1)Изменение элемента: O(n)
G>2)Взятие подмасиива: O(n)
G>3)Добавление\удаление элемента: O(n)
G>4)и только взятие элемента по индексу: O(1)
Точно так же как и в родном массиве (кроме возможности изменения элемента).
Да, но проблема не в этом, а в необходимости колдовать над каждым внутреннем массивом, что-бы ни дай бог кто-то не изменил его.

G>Фактически immutable массив эффективен только в случае доступа на чтение по случайному индексу.

Он был бы эффективен в плане удобства использования в контрактах при отсутсвии оверхедов оберток.
Re: Только мне в дотнете не хватает неизменяемого массива?
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 31.01.11 10:26
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Только мне в дотнете не хватает неизменяемого массива?

В джаве вон с 1999 по 2008 обсуждение шло — надо оно или нет. Так и не решили...
El pueblo unido jamás será vencido.
Re[2]: Только мне в дотнете не хватает неизменяемого массива
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 31.01.11 10:27
Оценка:
Здравствуйте, bl-blx, Вы писали:

BB>Здравствуйте, VladD2, Вы писали:


VD>>Только мне в дотнете не хватает неизменяемого массива?

BB>В джаве вон с 1999 по 2008 обсуждение шло — надо оно или нет. Так и не решили...
Там достаточно часто про неизменяемый массив упоминают.
El pueblo unido jamás será vencido.
Re[11]: Только мне в дотнете не хватает неизменяемого массив
От: Kalina9001  
Дата: 31.01.11 11:09
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Меня не очень интересуют языки. Меня интересует фича в рантайме. "const" мне тоже не нужен.

А я бы от "const" не отказался бы
... << RSDN@Home 1.2.0 alpha 4 rev. 1478>>
Re[2]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 15:25
Оценка:
Здравствуйте, xvost, Вы писали:

VD>>Только мне в дотнете не хватает неизменяемого массива?


X>А как ты его создавать и инициализировать собираешься? Через "ImmutableArrayBuilder" какой-нибудь???


Технически, т.п. на уровне рантайма с помощью функции конструктора. Скажем вводится некий атрибут которым можно пометить функцию возвращающую массив такого типа. Ну, а языках уже для этого можно свой синтаксис иметь. Скажем у немерла создание массивов выгядит так: array[1, 2, 3]. Некоторый набор таких предопределенных функций можно заложить в стандратную библиотеку. Например, можно создать функцию принимающую IEnumerable<T> и возвращающую неизменяемый массив.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 15:28
Оценка:
Здравствуйте, Пельмешко, Вы писали:

VD>>Ну, вот видишь в самом функциональном из функциональных языков таки есть.


П>На то он и чистый блин.


Он ни фига не чистый, блин. Тебя обманули. Он ленивый, блин. Но на наличие императивных конструкций это влияет весьма опосредовано.

П>В OCaml изменяемые массивы введены чисто для императивной стороны языка, фактически чтобы писать на нём числодробилки адекватной производительности.


Я никогда не считал ОКамл образцом для подражания.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 15:39
Оценка: +1
Здравствуйте, Аноним, Вы писали:

VD>>Не говоря уже, что второй подход — это почти первый замечу только, что оба подхода создают ощутимый и совершенно бесполезный оверхэд.


А>Какой оверхед ?


Самый обычный. Вместо создание одного объекта нужно создавать два. Учитывая, что даже на х86 объект занимает минимум 16 байт для маленьких массивов мы получаем оверхэд по памяти сравнимый с объемом хранимых данных.

Потом создание объектов — это атоммарная операция, а создание и инциализация двух объектов уже нет. Стало быть менеджер памяти вынужден делать блокировки/интерлоки и вставлять write-барьеры на указатели. А ведь их можно бы и не быть.

А>Напиши обертку которая бы не делала копирование, в чем проблема то ? Виртуальные вызовы оверхед ощутимый не создают.


Проблема в том, что таких объектов довольно много. А по сути на них можно и не тратить ресурсы. Отсюда мы имеем приципиальное отставание перед такими низкоуровневыми языками как С/С++. Причем отставание это обусловлено не объективными причинами, а просто просчетами в дизайне системы.

А>Иначе quake3 и друиге игры не писались бы с подходом ООП.


К твоему сведению, все версии Quake-ов (по крайней мере до версии 3 включительно) писались на С. Кармак (лидер ID Software) как раз таки недолюбливает ООП. По крайней мере он раз плохо высказывался об DirectX.

А>А фича реквесты есть намного более важные, чем готовые обертки в 10 строк для ленивых.


Ты просто пока не дорос до понимания важности наличия правильного набора базовых "строительных" блоков. Надеюсь, что со временем дорастешь.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 15:43
Оценка:
Здравствуйте, xvost, Вы писали:

X>И имеем в чистом виде копирование данных.


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

Строками ты ведь пользуешься? Не мешает, что они не изменяемые?

Вот и тут тоже самое. Для формирования массивов удобно использовать билдеры, а результата превращать в неизмеяемую структуру и пользоваться ею как угодно не боясь последствий. Плюс еще и скорость увеличить за счет открывающихся возможностей по оптимизации.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Только мне в дотнете не хватает неизменяемого массива
От: Ziaw Россия  
Дата: 31.01.11 15:51
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>1)Изменение элемента: O(n)

Да, но n у них достаточно маленький.

G>2)Взятие подмасиива: O(n)

Можно спроектировать и как О(1), этож иммутабельный массив.

G>3)Добавление\удаление элемента: O(n)

Как и в обычном массиве
Re[3]: Только мне в дотнете не хватает неизменяемого массива
От: _FRED_ Черногория
Дата: 31.01.11 15:55
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>>>Только мне в дотнете не хватает неизменяемого массива?


Не хватает часто Но всегда удавалось обойтись ReadOnlyCollection<>

QL>>Array.AsReadOnly();

QL>>?
VD>Массив — это монолитная структура данных. А AsReadOnly создает ReadOnlyCollection, что совершенно не нужный оверхэд, …

И всё-таки: где и как можно использовать "неизменяемый массив", что бы его нельзя было бы заменить ReadOnlyCollection? В голову только вариантность и приходит. Но это можно решить на уровне библиотеки.

А оверхед — ну разве что в названии типа, да он редко когда используется.

VD>…да и исходный массив он от изменения не защищает.


Ну так просто надо как можно скорее исходный "обернуть" и не будет этой проблемы. В крайнем случае ("как можно скорее не удаётся") скопировать (ничего тут не проиграем по сравнению со всеми другими реализациями)
Help will always be given at Hogwarts to those who ask for it.
Re[7]: Только мне в дотнете не хватает неизменяемого массива
От: Sinix  
Дата: 31.01.11 15:55
Оценка:
Здравствуйте, samius, Вы писали:

G>>Фактически immutable массив эффективен только в случае доступа на чтение по случайному индексу.

S>Он был бы эффективен в плане удобства использования в контрактах при отсутсвии оверхедов оберток.

Блин, ребят, вы определитесь — вы или за чистоту идеи, или за эффективность? 7 страниц трёпа как в КСВ

Не, отдельным товарищам понятно зачем — пока не докажешь, что дотнет отстой, а все окружающие — идиоты, своё как-то не попиаришь. А вот если померить — окажется, что R/O обёртка добавляет к себе цену virtual call — как раз примерно 3x стоимости доступа по индексу. Увы, в реальном приложении экономия на спичках тут же сожрётся вызовами других методов.

Output:
-------
array:
  Elapsed:      00:00:00.8285260, MemDelta: 0,01MB
  Elapsed real: 00:00:00.8300474, GC count: 0
-------
R/O collection:
  Elapsed:      00:00:03.8450174, MemDelta: 0,01MB
  Elapsed real: 00:00:03.8452199, GC count: 0
-------
Linq sum:
  Elapsed:      00:00:03.7079820, MemDelta: 0,01MB
  Elapsed real: 00:00:03.7072120, GC count: 0
-------
R/O - linq sum:
  Elapsed:      00:00:03.6998820, MemDelta: 0,01MB
  Elapsed real: 00:00:03.7002116, GC count: 0
Done. Press any key to exit...

  Код
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;

namespace ConsoleApplication1
{
  internal class Program
  {
    static void Main(string[] args)
    {
      int count = 10 * 1000 * 1000;
      int repeatCount = 50;

      int[] data = new int[count];
      Random rnd = new Random(0);
      for (int i = 0; i < count; i++)
      {
        data[i] = rnd.Next(10);
      }

      ReadOnlyCollection<int> roData = Array.AsReadOnly(data);

      Measure("array", () =>
      {
        for (int j = 0; j < repeatCount; j++)
        {
          long sum = 0;
          for (int i = 0; i < data.Length; i++)
          {
            sum += data[i];
          }
        }
      });

      Measure("R/O collection", () =>
      {
        for (int j = 0; j < repeatCount; j++)
        {
          long sum = 0;
          for (int i = 0; i < roData.Count; i++)
          {
            sum += roData[i];
          }
        }
      });

      Measure("Linq sum", () =>
      {
        for (int j = 0; j < repeatCount; j++)
        {
          int sum = data.Sum();
        }
      });

      Measure("R/O - linq sum", () =>
      {
        for (int j = 0; j < repeatCount; j++)
        {
          int sum = roData.Sum();
        }
      });

      Console.Write("Done. Press any key to exit...");
      Console.ReadKey();
    }

    public static void Measure(string message, Action callback)
    {
      long mem = GC.GetTotalMemory(true);
      long gcCount = GC.CollectionCount(0);

      DateTime now = DateTime.Now;
      Stopwatch stopwatch = Stopwatch.StartNew();
      callback();
      TimeSpan elapsed = stopwatch.Elapsed;
      TimeSpan elapsedReal = DateTime.Now - now;

      // DONTTOUCH:
      //  It is intended to call GetTotalMemory with forceFullCollection: false
      //  _before_ GC.CollectionCount call.
      double memDelta = (GC.GetTotalMemory(false) - mem) / (1024 * 1024.0);
      long gcCountDelta = GC.CollectionCount(0) - gcCount;

      Console.WriteLine(
@"-------
{0}:
  Elapsed:      {1}, MemDelta: {2:#,0.00MB}
  Elapsed real: {3}, GC count: {4}",
        message, elapsed, memDelta, elapsedReal, gcCountDelta);
    }
  }
}

Нашли чем меряться...
Re[2]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 15:56
Оценка:
Здравствуйте, xvost, Вы писали:

X>Да, и еще:

X>Сейчас все массивы являются реализацие класса System.Array.
X>Какой базовый класс предлагаешь ты для иммутабельных массивов, и как он будет соотноситься с классом System.Array в терминах ООП?

На самом деле текущая реализация массивов с их виртуальным наследованием от System.Array — это результат кривого дизайна и приспосабливания имеющихся до дженерик-эры сущностей.

Если бы была моя воля, то я бы вообще избавил бы от встроенных типов данных и эмуляции System.Array.

Я бы поступил следующим образом:
1. Ввел бы базовый тип System.Array[T].
2. Создал бы двух его наследников System.MutableArray[T] и System.ImmutableArray[T].
3. Ввел бы концепцию "хвостовых массивов". Концепция очень простая. Разрешаем объявлять в конце любого класса массив память под который выделяется в рамках области памяти отведенной под основной объект. При этом физически формат будет очень простой сначал должно идти 32-битное поле "длинна", а затем элементы массива.
4. Ввести в рантайм признак неизменяемости для хвостовых массивов (что-то типа readonly, но распространяемого на хвостовые массивы).
6. Реализовать System.MutableArray[T] и System.ImmutableArray[T] с использованием хвостовых массивов при этом пометив хвостовой массив в System.ImmutableArray[T] как неизменяемый.
7. Ввести некий атрибут позволяющий помеченному им методы-инициализаторы. В рамках таких методов будет доступна инициализация поэлементная System.ImmutableArray[T], но после выхода управления из таких методов массив уже больше не может подвергаться модификации.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 16:03
Оценка: +3 :)
Здравствуйте, samius, Вы писали:

S>Гораздо интереснее как он будет соотноситься с классом System.Array в терминах ковариантности. Точнее это не сильно интересно, а должно иметь место. Т.е. нужно что бы изменяемый массив можно было подавать в качестве неизменяемого.


Я считаю, что ковариантность для изменяемых массивов — это грубейшая ошибка дизайна! А возможность подстановки (без копирования) изменяемых массивов на место не изменяемых еще большая ошибка!!!

Поступить надо так. Ковариантность и контравариантность надо разрешить только для неизменяемых массивов. Для изменяемых ее надо запретить. Все равно ею мало кто пользуется. А если пользуется, то скорее всего не меняет содержимого. Например, методы вроде em.Windows.Forms.ListBox.Items.AddRange(object[] items) должны быть изменены на принимающие неизменяемые массивы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 16:04
Оценка:
Здравствуйте, samius, Вы писали:

S>Я понял мысль. Но почему бы в таком случае проверяющему не выполнять проверку через GetType()? Хотя тип в рантайме тоже можно подменить unsafe-ом...

S>Да, надо подумать об этом.

Просто не надо химичить. В подобных случаях проще скопировать массив. Это не так дорого как кажется.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 16:07
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>ЗЫ. А кто мешает в качестве низкоуровневневых строительных блоков применять обычные массивы?


А вот их изменяемость и мешает.

G>Главное не отдавать их наружу.


А вот будь не изменяемые массивы их можно было бы смело отдавать наружу без копирования.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 16:09
Оценка:
Здравствуйте, samius, Вы писали:

S>Это все понятно, но прежде всего такая фича не востребована массами. Если бы она действительна была нужна массам но ее бы не было, то тогда можно было бы ссылаться и на эти причины.


Дык менталитет масс формируют как раз те кто делает фичи (точнее не делает). Так что невостребованность закономерна.
Когда начинаешь плотно программировать на гибридном языке, то понимаешь, что фича нужна. А до этого действительно необходимости не чувствуется. Голова же думает исключительно теми шаблонами которые в нее заложили.

G>>ЗЫ. А кто мешает в качестве низкоуровневневых строительных блоков применять обычные массивы? Главное не отдавать их наружу.

S>То их и мешает использовать, что их надо тщательно прятать.

+1
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Только мне в дотнете не хватает неизменяемого массива
От: Sinix  
Дата: 31.01.11 16:11
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Я бы поступил следующим образом:

VD>1. Ввел бы базовый тип System.Array[T].
Как быть с Legacy, работающим именно с Array? Как описывать многомерные массивы?


VD>2. Создал бы двух его наследников System.MutableArray[T] и System.ImmutableArray[T].

Каст от одного к другому — копированием? Тогда вызов метода, принимающего ImmutableArray будет неэффективным.

Обёрткой? Тогда — никаких гарантий, что многопоточный код не изменит значения в ячейках массива.
Наконец, что делать, если значения в массиве — mutable?

VD>3. Ввел бы концепцию "хвостовых массивов".

Что это даст и в каких сценариях будет полезно? В 99.(9)% доступ к массиву осуществляется не напрямую, так что выигрыш будет съеден стоимостью вызова ацессора.

VD>4. Ввести в рантайм признак неизменяемости для хвостовых массивов (что-то типа readonly, но распространяемого на хвостовые массивы).

VD>6. Реализовать System.MutableArray[T] и System.ImmutableArray[T] с использованием хвостовых массивов при этом пометив хвостовой массив в System.ImmutableArray[T] как неизменяемый.
Зачем? Практически везде внутренняя реализация массивов отдана на откуп рантайму. Добавим уровень абстракции — пожертвуем производительностью и огребём проблем с интеропом.

VD>7. Ввести некий атрибут позволяющий помеченному им методы-инициализаторы. В рамках таких методов будет доступна инициализация поэлементная System.ImmutableArray[T], но после выхода управления из таких методов массив уже больше не может подвергаться модификации.

И ещё добавить трассировку — чтобы метод не вызвали более одного раза. И кидать исключения при попытке обратиться к элементам до инициализации.

Не много ли костылей мы ввели ради абстрактной чистоты?
А сколько придётся вводить для многомерных массивов/массивов с ненулевой индексацией?
Re[6]: Только мне в дотнете не хватает неизменяемого массива
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.01.11 16:18
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Но ведь не в этом проблема, а в быстродействии.


Проблема в том числе и в быстродействии. Но основная проблема в необходимости лишних абстракций.

G>Для immutable массивов будут следующие операции и их асимптотика

G>1)Изменение элемента: O(n)
G>2)Взятие подмасиива: O(n)
G>3)Добавление\удаление элемента: O(n)
G>4)и только взятие элемента по индексу: O(1)

Вот ты спрашивал "зачем?", но как тебе объяснить "зачем?", если ты мыслишь императивно.

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

В ФЯ где нет неизменяемых массивов используют связанные списки, так как они не изменяемые. Это довольно удобно (за тем исключением, что доступ по индексу требует O(n). Но это весьма затратно, так как на каждый элемент списка приходится создавать по отдельному объекту. Объект в дотнете — это минимум 12 байт оверхэда (в х64 бошльше). Конечно и так можно жить, но не ясно зачем платить больше?

G>Фактически immutable массив эффективен только в случае доступа на чтение по случайному индексу.


Фактически неизменяемые массивы всегда эффективнее чем изменяемые, если изменяемые массивы используются для хранения и не меняются. Эффективнее неизменяемые массивы и тем, что не требуется оберток для их защиты, и тем что для их управления не нужен сложных GC с барьером записи, и тем что имеются железные гарантии отсутствия ошибок связанных с изменением.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.