LINUX.ORG.RU

Кольцевой двухсвязный список ограниченного размера

 dllist,


0

2

Собственно, пишу соотв. класс (пока для Perl). Мне он нужен для того, чтобы задания, поставленные в очередь отправки, в случае успешной отправки выдёргивать из списка-кольца. То, что одно задание отправится - не гарантирует успеха с остальными, поэтому нельзя очищать просто N элементов подряд из очереди: есть вероятность того, что какое-то задание удастся грохнуть из списка, а какие-то придётся оставить до следующей попытки.

Вопрос к тому, что всегда интересно посмотреть на чужую имплементацию, чтобы у себя не делать наивную реализацию, а сразу сделать правильно.

У меня реализация с 2-мя структурами данных: стек свободных элементов и кольцевой двухсвязный список, к которому есть отдельная переменная-указатель на ноду-голову списка (управляет добавлением новых элементов). «К голове» либо создаётся и привязывается новый узел (будущая голова), если ссылка на следующий элемент ведёт в NULL, либо происходит перезапись ссылки на данные в том элементе, на который голова указывает как на следующий и происходит смещение указателя головы на этот следующий элемент (за счёт этого старые данные по достижении границ буфера перетираются новыми).

Стек нужен для того, чтобы брать из него индексы свободных ячеек списка нод двухсвязного кольца и пушить туда эти индексы при высвобождении соотв. нод.

Условно - если размер кольца 5 элементов, то на стеке изначально будут лежать : [4,3,2,1,0] - и первый pop возьмёт индекс 0, создаст в nodes_list[0] узел с NULL-ссылками на next и prev ноды.

Может, на Ruby что-то такое есть, например? Ну или ещё на каком языке с не-Rust и не-C++ подобным синтаксисом?

★★★★★

Последнее исправление: DRVTiny (всего исправлений: 3)

Ответ на: комментарий от DRVTiny

Прошу прощения. Третий день язык чешется спросить - а нельзя ли просто массив структур вида {флаг_состояния, адрес_задания} и бегать по нему с опросом флагов «свободен», «выполняется», «ожидает»? Или что плохого в таком варианте? Список принципиально важен для задачи или для Perl?

Deleted
()
Ответ на: комментарий от DRVTiny

Тебе прямую ссылку на реализацию сплайса выше дали, со зрением все хорошо, дядя? Какие св.списки, завязывай с веществами, или хотя бы не пиши под ними. Осиль тот факт, что копировать (dst++ = src++) даже 100кб быстрее, чем вся эта мотня с ручной кучей нод. Как минимум на перле, что ты и имел наблюдать.

anonymous
()
Ответ на: комментарий от OxiD

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

OxiD ★★★★
()
Ответ на: комментарий от OxiD

Я не видел код, реализующий списки на Perl, но подозреваю, что там есть индексирование списков, иначе бы они работали просто безумно медленно. Для каждого списка должен быть по идее вектор смещений элементов

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от Deleted

Не очень понимаю, о чём речь. Мне это нужно было для очереди отправки сообщений. С интервалом P по очереди проходит функция, которая отправляет сообщения из очереди. Отправилось - хорошо, удаляет сообщение из очереди. Не отправилось - делает ++ числа попыток, при достижении порога - тоже удаляет из очереди.

DRVTiny ★★★★★
() автор топика

Кстати, а может быть есть для Perl'а правильные векторы, которые всё-таки последовательность данных фиксированного размера, характеризуемая количеством элементов данных и размером одного элемента?

Т.е. не чудесные списки, что бы там в них ни было, а настоящие статичные векторы?

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от anonymous

А что, анонимусы почитывают на ночь исходные тексты интерпретатора Perl? Однако, ЛОРовские анонимусы весьма высокоинтеллектуальны.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Treats the string in EXPR as a bit vector made up of elements of width BITS and returns the value of the element specified by OFFSET as an unsigned integer. BITS therefore specifies the number of bits that are reserved for each element in the bit vector. This must be a power of two from 1 to 32 (or 64, if your platform supports that).

Ты это серьёзно предлагаешь использовать или прикалываешься?

И как бы ничего, что строка во внутреннем представлении - тоже список? Что даёт в таком случае использование vec'а? Даже если абстрагироваться от кошмара на уровне посложнее ассемблера - как vec избавляет от списков и накладных расходов, связанных с ними?

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Сарказм насчёт splice предлагаю всё-таки засунуть себе в ж-пу: я для тупых уже раз 20 объяснил, что только благодаря этой теме понял, что perl никогда не хранит списки как вменяемые статичные вектора - раньше да, я предполагал, что splice будет копировать память, и старался им не пользоваться. Но где о принципах работы splice'а на низком уровне что-либо сказано в документации? Что я должен был «выучить»? Пока я не сделал бенчмарк для сравнения производительности splice и пересоздания списка - я понятия не имел о том, что есть колоссальная разница.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

ты хотел

последовательность данных фиксированного размера, характеризуемая количеством элементов данных и размером одного элемента

и получил это, а теперь ноешь что слишком статично и низкоуровнево?..

anonymous
()
Ответ на: комментарий от DRVTiny

можно было совсем немного пошевелить мозгами и догадаться, что CORE::splice работает оптимально, а не пересоздаёт массив на каждый вызов

anonymous
()
Ответ на: комментарий от anonymous

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

Элементы справа от вырезанных копируются со сдвигом на «размер выреза». Другое дело, что динамический массив хранит не сами данные, что было бы странно, а ссылки на данные, поэтому формально копируется небольшой объём... Но если список огромный а вырезается что-то ближе к началу, то копирования памяти могут быть очень даже заметными.

Я одного не понимаю: что мешало разработчикам perl'а всё-таки сделать нормальные array фиксированного размера - с проверкой границ, отсутствием возможности что-то туда пушить и сплайсить, зато максимально быстрые?

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Я не ною. Я констатирую факт: синтаксис работы с такой хренью весьма у*бищен, и это действительно невозможно будет поддерживать. Всё-таки для массивов должен быть синтаксис массивов или хотя бы объектный.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

что мешало разработчикам perl'а всё-таки сделать нормальные array фиксированного размера - с проверкой границ, отсутствием возможности что-то туда пушить и сплайсить, зато максимально быстрые?

В чём проблема создать массив фиксированного размера и самому себе запретить всё это делать?

anonymous
()
Ответ на: комментарий от anonymous

Ну вот я запретил всё это себе делать, а в итоге меня говном здесь закидали. И ещё убедили в том, что списки в perl - это реально списки, хотя это в действительности ни разу не так (динамические массивы). В результате же и splice память копирует, и push запросто может приводить к тому, что в 99-ти случаях всё будет очень быстро, а 100-й push приведёт к malloc'у с огромным запасом и массированному копированию памяти, а потом к free при очередном проходе счётчика ссылок. Честно, мне не нравятся такие вещи, я за предсказуемость любой операции по времени её выполнения.

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от DRVTiny

Если интересуют подобные микрооптимизации (читай оверинжиниринг), то пиши реализацию на С/XS.

anonymous
()
Ответ на: комментарий от DRVTiny

Если требуется эффективный брокер сообщений, то проще взять zeromq (Perl-биндинги в наличии).

anonymous
()
Ответ на: комментарий от anonymous

У меня брокером Redis, но что-то степень надёжности публикации сообщений в него далека от 100%

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Не, там уж слишком много желающих долбать один Redis, сессия подолгу висит, а сами публикуемые сообщения - по 16Мб. Не очень много конечно, но как-то не всегда они успешно публикуются. Хорощо хоть это отследить легко - и в случае возникновения ошибки я их пинаю в очередь unpublished :) Но идея в том, чтобы даже если Redis вообще рухнет - можно было хотя бы последние N сообщений накопить в очереди и как только поднимется - попытаться все их ему скормить.

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от DRVTiny

Мда.. и каким может быть макс N учитывая размер отправляемого по сети сообщения в 16Мб? 1000? Какой смысл ради 1000 (пусть даже 1000000) городить этот dll-велосипед, вместо обычных @-массивов. Неужели не понятно, что bottleneck в другом месте.

anonymous
()
Ответ на: комментарий от anonymous

Суть в том, что раз есть такой отличный повод - почему бы не написать качественную библиотеку и для Perl, и для того же Crystal. У меня полно библиотек, которые создавались «по случаю» и нашли применение во многих проектах. Собственно, если есть время, мне всегда интересно написать продуманный универсальный код. Но да, в случае с perl'ом здесь куда ни кинь - всё одно либо на Си писать, либо встроенные средства при всей их прожорливости всё равно будут быстрее работать.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Суть в том, что раз есть такой отличный повод - почему бы не написать качественную библиотеку и для Perl, и для того же Crystal

Надеюсь что на досуге, иначе так себе сотрудник получается.

У меня полно библиотек, которые создавались «по случаю» и нашли применение во многих проектах.

Понятно.. На cpan/github есть что-то из этих «качественных библиотек»?

anonymous
()
Ответ на: комментарий от DRVTiny

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

Мне кажется ты тролль )

OxiD ★★★★
()
Ответ на: комментарий от anonymous

На github'е есть, что не лень было выложить, на CPAN не выкладываю, поскольку пишу для себя/для работы в основном.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Надеюсь что на досуге, иначе так себе сотрудник получается.

А не «так себе» что делает? Бездумно х*рачит код за еду?

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от OxiD

Сначала у тебя было:

Ты знаешь какая сложность обращения по индексу у массива и у списка? Если да, то ты можешь несложным экспериментом доказать, что в перле в @ внутри список а не вектор :)

А если не знаешь, то наверное пришло время выяснить

А теперь:

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

Определись что ли, а?

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

А не «так себе» что делает? Бездумно х*рачит код за еду?

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

anonymous
()
Ответ на: комментарий от OxiD

Ты сделал 2 взаимно противоречащих друг другу утверждения - это немного странно или я чего-то недопонял?

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Долго думал что тебе ответить. Так и не придумал :-)

OxiD ★★★★
()
Ответ на: комментарий от OxiD

Какой эксперимент? Чего это ты теперь пост-фактум пытаешься на ходу менять показания?

Эксперимент сравнения реализации списков в Perl'е с отсутствующей реализацией статичных одномерных массивов? Да ну? И как я это должен был сравнивать?

Или сравнить списки, которые @ со списками, которые одно- и двухсвязные и которых нет в perl'е в принципе, и в XS-ках тоже нет (List::DoubleLinkedList всё-таки использует нативный код на Си)? Кстати, интересный пост на эту тему: http://www.perlmonks.org/?node_id=651271

Или сравнить реализацию списков (пусть даже двухсвязных) внутри Perl'а и на самом Perl'е? Ну так я тебе итак могу сказать, что разница в скорости работы реализации одной и той же структуры perl'ом и XS'ом может различаться в сотни раз - как минимум потому что сам интерпретатор Perl'а работает очень не быстро, да и само внутреннее представление структур для списков будет немного разным.

Сравнивать тут абсолютно нечего, сравнить можно собственно только вырезание элементов из середины списка разными способами. splice тут конечно даёт результаты куда более убедительные, нежели «прямолинейный» код на perl'е, хотя надо бы попробовать ещё варианты этого самого нативного кода (срезы массивов) на досуге.

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 4)
Ответ на: комментарий от OxiD

Интересно, как ты видишь удаление из середины вектора/списка, если это удаление реализуется в асинхронных обработчиках.

Смотри:

0) Мы находимся внутри асинхронного коллбека (вызван таймером)

1) Мы пытается отправлять задания из очереди, начиная со «старейшего»

2) Для этого мы используем асинхронный вызов «опубликуй сообщение» с callback'ом, так что callback замыкает в себе переменную - идентификатор задания

3) Внутри callback'а: удаляем задание из списка, если, судя по коду возврата, оно наконец успешно отправилось.

Так вот, как удалить splice'ом? Сейчас я использую для очереди хеш с ключами вида «метка времени из Time::HiRes::time()».«int(rand(10000))» - это гарантирует корректность delete($queUnPub{$key}). Но при этом не даёт легко и быстро регулировать количество элементов в хеше.

По-моему никак: единственный вариант был бы: породить кучу асинхронных вызовов, после чего заснуть, ожидая завершения их всех. В принципе возможно - в том же Mojo::IOLoop::Delay вполне себе можно сделать wait. Вопрос только в том, что происходит во время этого wait'а... Если это блокировка, то я лучше застрелюсь, чем так сделаю когда-либо. Если же это просто передача управления другим обработчикам до тех пор, пока всё порожденное IOLoop::Delay'ем не завершится - тогда можно в каждом callback'е запушить индекс удаляемого элемента в некий вектор, а после wait - сделать splice по всем соотв. элементам. Печально только то, что так легко попасть в ситуацию гонки: очень плохо, когда элементы идентифицируются только индексом в массиве, так легко удалить что-то совсем не то.

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 2)
Ответ на: комментарий от DRVTiny

Приведи пожалуйста ссылку на сору перла где будет видно что массивы сделаны в виде какого то там списка. До тех пор не вижу смысла продолжать дискуссию. И таки да доказывать утверждение должен тот кто его высказывает. Поэтому давай, вреред

OxiD ★★★★
()
Ответ на: комментарий от OxiD

Так это же на основании твоего утверждения я и сделал такой вывод! Ну и плюс провёл тест (не самый показательный на самом деле), который вроде как доказал, что splice подозрительно быстр. В действительности же это интерпретация кода на Perl, мягко говоря, не грешит оптимизациями.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от DRVTiny

Но если список огромный а вырезается что-то ближе к началу, то копирования памяти могут быть очень даже заметными.

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

anonymous
()
Ответ на: комментарий от DRVTiny

Что за ад. Можно просто «замыкать» взятый из очереди таск и в случае фейла класть его обратно в очередь. google it: KISS, YAGNI, DTSTTCPW или хотя бы вспомни бритву Оккама.

Вопрос только в том, что происходит во время этого wait'а... Если это блокировка, то я лучше застрелюсь

Больше жести! Доки не читай - предполагай.

anonymous
()
Ответ на: комментарий от anonymous

Я не собираюсь читать исходный код интерпретатора, да ещё написанный на языке Си. Мне кроме этого есть чем заняться.

DRVTiny ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Если вырезается ближе к началу, то все начало сдвигается вправо и увеличивается указатель на начало.

Ты тупой и читать не умеешь. Прочитай ещё раз, что написал я - и подумай, что здесь не так. Не хочешь думать или не умеешь - мне по***.

DRVTiny ★★★★★
() автор топика

По делу, если я правильно понял. Я бы сделал Хешик ключ - название состояния. Значение - хешик по id задания. Когда задание готово оно перекладывается в другой раздел с соответствующим статусом. Вообще на перл лучше пользоваться по максимуму встроенными структурами. Деревья, самопальные списки обычно говорят об оверинженеринге

OxiD ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.