List<T>.MoveToArray()
От: Qbit86 Кипр
Дата: 02.11.18 07:54
Оценка:
Скажите, вам бы пригодился метод List<T>.MoveToArray(), будь он в стандартной поставке? Конечно, это должно заботить только тех, кто волнуется о количестве и объёме аллокаций — т.е. в общем случае мало кого. Но тем не менее.
Семантика: отказаться от владения низлежащим буфером и передать его наружу. Используется как array builder для интеропа с низкоуровневыми API, которые получают на вход сегмент массива.

Пример использования:
var localList = new List<string>();
// Populating, filtering, sorting, grouping...
localList.Add("Camille");
localList.Add("Annie");
localList.Add("Sara");
localList.Add("Katrin");
localList.Add("Kari");

string[] array = localList.ToArray(); // Want to move, not to copy.
string grouping = string.Join(", ", array, 1, 3);
Console.WriteLine(grouping); // Annie, Sara, Katrin


Если это кажется полезным, присоединяйтесь к обсуждению: https://github.com/dotnet/corefx/issues/33169 Пока что собираются предложение завернуть. (Может, и правильно.)
Глаза у меня добрые, но рубашка — смирительная!
Re: List<T>.MoveToArray()
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 02.11.18 08:23
Оценка: 1 (1)
Здравствуйте, Qbit86, Вы писали:


Q>Если это кажется полезным, присоединяйтесь к обсуждению: https://github.com/dotnet/corefx/issues/33169 Пока что собираются предложение завернуть. (Может, и правильно.)

По мне так полезная фича, ибо куча методов которая требует масиив и приходится копировать.
и солнце б утром не вставало, когда бы не было меня
Re[2]: Куча методов
От: Qbit86 Кипр
Дата: 02.11.18 08:28
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>По мне так полезная фича, ибо куча методов которая требует масиив и приходится копировать.


Можешь повлиять :) Достаточно откомментироваться в обсуждении (опционально со своими примерами), чтобы потихоньку склонить decision maker'ов к востребованности фичи. А там я запилю PR, и десятка лет не пройдёт, как замерджат и внедрят :)
Глаза у меня добрые, но рубашка — смирительная!
Отредактировано 02.11.2018 8:30 Qbit86 . Предыдущая версия .
Re: List<T>.MoveToArray()
От: romangr Россия  
Дата: 02.11.18 08:41
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Скажите, вам бы пригодился метод List<T>.MoveToArray(), будь он в стандартной поставке? Конечно, это должно заботить только тех, кто волнуется о количестве и объёме аллокаций — т.е. в общем случае мало кого. Но тем не менее.

Q>Семантика: отказаться от владения низлежащим буфером и передать его наружу. Используется как array builder для интеропа с низкоуровневыми API, которые получают на вход сегмент массива.

Я большого смысла в таком API не вижу.
Если бы чего и хотелось, то это вариант Array.Resize(T[], Int32), который при новом размере меньше текущего не копировал бы массив в новый, а каким-то образом возвращал неиспользуемую память системе.
Тогда можно было бы использовать вместо List<T> ArrayBuilder<T> и затем просто обрезать массив до нужного размера.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re: List<T>.MoveToArray()
От: pugv Россия  
Дата: 02.11.18 09:52
Оценка: +1
А зачем оно в стандарте? Своя обёртка над массивом и пинпоинтеры для передачи в unmanaged без маршалинга — пара десятков строк кода. К тому же для тех, кто заботится о количестве и объёме аллокаций, стандартный List всё равно не лучший выбор.
Re[2]: Зачем оно в стандарте
От: Qbit86 Кипр
Дата: 02.11.18 09:57
Оценка:
Здравствуйте, pugv, Вы писали:

P>А зачем оно в стандарте?


Потому что а почему бы и нет? Это не новый функционал, это просто раскрытие существующего штатного, но скрытого функционала.

P>Своя обёртка над массивом и пинпоинтеры для передачи в unmanaged без маршалинга — пара десятков строк кода.


В существующих кодобазах уже полно всяких временных листов, начинающихся от `.ToList()` с последующими модификациями. Создание своего handmade ArrayBuilder'а не отменит того факта, что в дефолтной коллекции List<T> нет базового функционала.
Глаза у меня добрые, но рубашка — смирительная!
Re: List<T>.MoveToArray()
От: _FRED_ Черногория
Дата: 02.11.18 18:55
Оценка: +2
Здравствуйте, Qbit86, Вы писали:

Q>Скажите, вам бы пригодился метод List<T>.MoveToArray(), будь он в стандартной поставке?


Показалось, что совершенно бесполезная вещь: если число элементов в коллекции изначально неизвестно (а такой вывод можно сделать по приведённому примеру), то при добавлении элементов в список уже будут случаться переаллокации и копирования и предлагаемый метод лишь уменьшит их число на единицу в чём профит?

Когда беспокоит количество и объём аллокаций (в смысле сведения их к абсолютному минимуму), имеет смысл подойти к проблеме иначе — или всё-таки быть осведомлёммым о числе (возможно, максимальном) элементов и тогда можно сразу массив и использовать или использовать другие структуры данных.
Help will always be given at Hogwarts to those who ask for it.
Re: List<T>.MoveToArray()
От: Sharowarsheg  
Дата: 02.11.18 19:01
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Скажите, вам бы пригодился метод List<T>.MoveToArray(), будь он в стандартной поставке? Конечно, это должно заботить только тех, кто волнуется о количестве и объёме аллокаций — т.е. в общем случае мало кого. Но тем не менее.


Те, кто заботятся о количестве аллокаций, напишут себе сами свой List, а скорее всего, напишут себе что-то другое, более интегированное. Так что нет, не пригодился бы.
Re: List<T>.MoveToArray()
От: hi_octane Беларусь  
Дата: 02.11.18 19:08
Оценка:
Q>Семантика: отказаться от владения низлежащим буфером и передать его наружу. Используется как array builder для интеропа с низкоуровневыми API, которые получают на вход сегмент массива.
Q>Пример использования:
Q>
Q>string[] array = localList.ToArray(); // Want to move, not to copy.
Q>string grouping = string.Join(", ", array, 1, 3);
Q>Console.WriteLine(grouping); // Annie, Sara, Katrin
Q>


Ты про все эти новые Span<T>, Memory<T>, Sequence<T> и Ranges которые или частично сделаны или в процессе, читал? Имхо все подобные сценарии давно закрыты целиком и полностью.
Re[2]: Свой List
От: Qbit86 Кипр
Дата: 02.11.18 19:12
Оценка:
Здравствуйте, Sharowarsheg, Вы писали:

S>Те, кто заботятся о количестве аллокаций, напишут себе сами свой List


Я себе свой List написал. Но хочется иметь дешёвый и естественный способ избежать заведомо ненужной пессимизации в существующих кодобазах, без привлечения лишних зависимостей.
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Сценарии давно закрыты
От: Qbit86 Кипр
Дата: 02.11.18 19:24
Оценка: +1
Здравствуйте, hi_octane, Вы писали:

_>Ты про все эти новые Span<T>, Memory<T>, Sequence<T> и Ranges которые или частично сделаны или в процессе, читал?


Да.

_>Имхо все подобные сценарии давно закрыты целиком и полностью.


Так «давно закрыты целиком и полностью» или «частично сделаны или в процессе»?

1) Куча существующих стандартных API по-прежнему получают на вход массивы и их сегменты, а не Span<T>. И все они переделаны в ближайшее время не будут, не говоря уже о сторонних библиотеках.
2) Если в существующей кодобазе где-то есть подобный временный список, то Span<T> ты из него не получишь, чтобы передать в подобный гипотетический API.
3) Memory<T> — это абстракция, со своими издержками. А MoveToArray() — это механика конкретной структуры данных, для неё естественная и практичная.
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Уменьшить на единицу
От: Qbit86 Кипр
Дата: 02.11.18 19:32
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>при добавлении элементов в список уже будут случаться переаллокации и копирования и предлагаемый метод лишь уменьшит их число на единицу :xz: в чём профит?


А почему бы и не уменьшить на единицу? Причём, это уменьшение в районе правой границы удвоений. То есть одна аллокация в этом диапазоне длин по объёму будет порядка суммы всех предыдущих аллокаций.

_FR>или всё-таки быть осведомлёммым о числе (возможно, максимальном) элементов и тогда можно сразу массив и использовать или использовать другие структуры данных.


Да, часто есть какая-то предварительная estimatedCapacity, так что может быть даже всего одна аллокация. И не хочется городить ещё одну чуть меньшего размера под массив, когда почти подходящий массив уже есть внутри, только руку протяни.
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Свой List
От: Sharowarsheg  
Дата: 02.11.18 19:34
Оценка: 3 (1)
Здравствуйте, Qbit86, Вы писали:

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


S>>Те, кто заботятся о количестве аллокаций, напишут себе сами свой List


Q>Я себе свой List написал. Но хочется иметь дешёвый и естественный способ избежать заведомо ненужной пессимизации в существующих кодобазах, без привлечения лишних зависимостей.


У меня гораздо более двоичный подход к оптимизации — или нужно оптимизировать, или нет. Если нет, то пойдет стандартный лист. Если да, то ничего из готового, скорее всего, не подойдет — а если подошло, то, скорее всего, ещё есть куда оптимизировать.
Re: List<T>.MoveToArray()
От: Mystic Artifact  
Дата: 03.11.18 08:32
Оценка: +1
Здравствуйте, Qbit86, Вы писали:

Мне не кажется это сильно полезным:

1. Нижележащие managed-библиотеки по идее должны принимать ровно то, что им нужно. Т.е. где-то IEnumerable достаточен, где-то ICollection и т.д.

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

Я банально встречал ошибки с MemoryStream где вместо ToArray брали нижележащий буффер и получали лишний хвост (что приводило к неверным данным).

Боюсь себе представить "типичное" использование подобного метода.


2. Ну, а для unmanaged — это и вовсе непроблема, т.к. тот же список строк придется все равно как-то развернуть в соотв. с API.
Re[2]: MemoryStream
От: Qbit86 Кипр
Дата: 03.11.18 08:59
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

MA>Я банально встречал ошибки с MemoryStream где вместо ToArray брали нижележащий буффер и получали лишний хвост (что приводило к неверным данным).

MA>Боюсь себе представить "типичное" использование подобного метода. :)

Пример с MemoryStream хороший. В отличие от MemoryStream.GetBuffer(), предложенный метод List<T>.ReleaseBuffer()'а не раскрывает внутренности экземпляра List'а, так как эти внутренности немедленно перестают ему принадлежать сразу после вызова. То есть «опасность» относиться к другим типам — самому массиву и тем API, которые его потребляют.

MA>Опять же — те кто принимают просто массив — могут не принимать длину и тут будет облом. :)


Для тех API, которые зависят от array.Length, а не от переданной длины, такой подход не будет работать. К нему transfer ownership неприменим, а приходится делать вынужденную копию. Но в остальных-то случаях (а их, полагаю, большинство) API принимают пару (array, count) или тройку (array, offset, count).
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Уменьшить на единицу
От: _FRED_ Черногория
Дата: 03.11.18 11:24
Оценка:
Здравствуйте, Qbit86, Вы писали:

_FR>>при добавлении элементов в список уже будут случаться переаллокации и копирования и предлагаемый метод лишь уменьшит их число на единицу в чём профит?


Q>А почему бы и не уменьшить на единицу?


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

Предлагаемый же метод меняет семантику такого широко используемого типа, как List<> за ради сильно небольшой выгоды. Лучше обратите внимание на то, сколько в BCL типов-аналогов List<>-а (обёрток над массивом, переаллоцируемым при необходимости), например тут. Для каждого конкретного случая самым улучшим оказывается что-то со своими маленькими особенностями и это кажется более правильным, чем нечто общее, но в большинстве случаев не самое лучшее.
Help will always be given at Hogwarts to those who ask for it.
Конкретный случай
От: Qbit86 Кипр
Дата: 03.11.18 11:44
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Предлагаемый же метод меняет семантику такого широко используемого типа, как List<>


Не меняет вообще никак, совсем. Просто возвращает в набор публичных API ожидаемый там естественный метод, который по какому-то упущению не добавили ещё в .NET 2.0.

_FR>Для каждого конкретного случая самым улучшим оказывается что-то со своими маленькими особенностями и это кажется более правильным, чем нечто общее, но в большинстве случаев не самое лучшее.


List<T>.ReleaseBuffer() — это и есть не «нечто общее», а то самое «что-то со своими маленькими особенностями» «для каждого конкретного случая». В данном случае для конкретного случая List<T>'а. Вот есть в кодобазе куча конкретных листов, и для этих конкретных случаев конкретных листов самым лучшим оказывается конкретная механика List<T>.ReleaseBuffer(), а не рихтование кода под ArrayBuilder{T}, который вернёт Buffer<T>, который кастится к Span<T>, и так далее.
Глаза у меня добрые, но рубашка — смирительная!
Отредактировано 03.11.2018 11:45 Qbit86 . Предыдущая версия .
Re: List<T>.MoveToArray()
От: keenn  
Дата: 04.11.18 04:52
Оценка:
ссылку не читал.

там что, правда предлагается костыль, который будет ради "интеропа с низкоуровневыми API" высовывать наружу "низлежащий буффер", меняя состояние до полной невалидности своего контейнера?

жесть какая-то, чего только не предложат
Re[2]: Нет
От: Qbit86 Кипр
Дата: 04.11.18 08:05
Оценка:
Здравствуйте, keenn, Вы писали:

K>там что, правда предлагается костыль, который будет ради "интеропа с низкоуровневыми API" высовывать наружу "низлежащий буффер", меняя состояние до полной невалидности своего контейнера?


Нет.
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Нет
От: keenn  
Дата: 04.11.18 08:45
Оценка:
Q>Нет.

Ну разжую тогда, раз "нет". Зашел по ссылке, там реализация предлагается такая (дальше опять читать не стал, наверное уже достаточно):

public T[] MoveToArray()
{
    T[] result = _items;
    _size = 0;
    _items = s_emptyArray;
    _version++;
    return result;
}


— предполагается, что клиенты List будут с абсолютной уверенностью ожидать, что под List точно должен быть именно array, на сто процентов. Вообще, клиент хорошо так погружается во внутренности List (в которые он и до этого немного излишне погружался). Можно и далее пойти "изящные" хаки делать — например, создавать List(capacity n) и далее в зависимости от ситуации делать либо MoveToArray (получая массив), либо дальше работать с листом. А, что? Красиво! capacity может не значить, что массив будет обязательно создан? (а должен значить! мы то знаем!) Нельзя на это полагаться? Или можно? Да что вы говорите.
— всегда могут положиться на этот костыль, думая, что "так они не потеряют в ресурсах" (быстродействии/памяти), когда хотят получить массив там, где им нужно. Ведь этот костыль в основном на "мамкину оптимизацию". "efficient “array builder”, епта (рукалицо). А нужно им это там, где они просто плохо продумали свой код изначально. И теперь хотят, чтобы им костыли дали на уровне стандарта. Ну офигеть теперь.
— название — просто сказка. Которая отлично характеризует эту инициативу. "MoveToArray". Можно там развить инициативу, ведь нам нужен эффисиент эррэй билдер! И поэтому добавляем в стандарт:
public T[] MoveToArray(int start, int length) — вырезает из массива частичку. А чего. Эффективно! Для нужд получения части массива, когда нужна только часть.
public T[] MoveToArray(int start, int length, bool noDelete) — возвращает часть массива, а "вырезанную" часть не удаляет, а контейнер путем хитрых махинацй указания отрезков потом переиспользует. А по индексам использует информацию по смещениям. Еще эффективнее!

Про то, что "предлагатель" забыл про capacity — я вообще молчу. Это ожидаемо чисто из уровня этого пропозала.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.