Перейти к содержимому


Фото

Разделяемый умный указатель с свистелками


  • Чтобы отвечать, сперва войдите на форум
13 ответов в теме

#1 Ripper Опубликовано 02 Апрель 2012 - 17:05

Ripper
  • Свои
  • 497 Сообщений:
  • Павел Рустанович
Нужно сделать так, что бы одновременно на один объект (структуру) либо существовали несколько read-only указателей, либо один read-write. Таким образом у каждого пользователя (хозяина указателя) имеется объект, который не изменяется без его ведома. Пользователь при этом может попросить доступ к объекту на редактирование, тогда, если других read-only указателей не осталось, объект просто достаётся ему, в противном случае делается копия. Существует ли что-то похожее, или может этому даже есть научное название?

Изменено: Ripper, 02 Апрель 2012 - 17:06

post-2756-0-28658900-1472313042.png


#2 KiberGus Опубликовано 03 Апрель 2012 - 5:14

KiberGus
  • Genius loci
  • 6 561 Сообщений:
  • Алексей Гусейнов
Конкретно такого не существует т.к. это несколько странный дизайн:

других read-only указателей не осталось, объект просто достаётся ему, в противном случае делается копия.

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

Кроме того, важный вопрос говорим ли мы о многопоточной программе. Я так понимаю, что да. Смотри на std::mutex из C++11 или boost::mutex,если у тебя старый компилятор.
Зато, обладая единственной в мире подводной орбитальной группировкой спутников глонасс...
gentoo.gif

#3 Ripper Опубликовано 03 Апрель 2012 - 5:43

Ripper
  • Свои
  • 497 Сообщений:
  • Павел Рустанович

Других указателей не осталось? Откуда тогда взять адрес объекта, если на него никто не указывает?

У пользователя уже есть read-only указатель, и если при этом других указателей на объект не существует, то пользователь может сделать из него read-write без копирования.

Объект - это какие-то данные, вроде изображения. Насколько мне известно, ничто похожее есть в php при копировании переменных (они фактически не копируются, а ссылаются на одни и те же данные до тех пор, пока какая-то из ссылающихся переменных не будет изменена).

post-2756-0-28658900-1472313042.png


#4 St.ALKer Опубликовано 03 Апрель 2012 - 7:09

St.ALKer
  • Свои
  • 59 Сообщений:
  • .
Насколько я понимаю ты говоришь о чём-то типа copy on write ( http://en.wikibooks....s/Copy-on-write ), который по слухам применяется в некоторых реализациях std::string. Но только ты ещё хочешь контролировать количество клиентов. В таком случае, если есть код самого объекта, ты можешь встроить счётчик ссылок туда (intrusive reference counter, http://en.wikibooks....ms/Counted_Body ), из мутаторов возвращать новый указатель, а решать копировать ли объект будет код мутатора - если refcount == 1, то мутировать in-place, если > 1 - клонировать и вызвать тот же метод. Тут конечно плохо, что надо не забыть сохрнить возвращённый указатель.
Intrusive reference counter есть в бусте ( http://www.boost.org...rusive_ptr.html ), мы же пользуемся кастомным удалятором в boost::shared_ptr, т.е. своим указателем, который внутри хранит shared_ptr, который в деструкторе зовёт метод объекта на который указывает, а тот уже решает удаляться или нет.
boost::shared_ptr поддерживает многопоточность, но всяко если предполагается несколько потоков надо код мутатора защищать.

#5 KiberGus Опубликовано 03 Апрель 2012 - 7:33

KiberGus
  • Genius loci
  • 6 561 Сообщений:
  • Алексей Гусейнов
На мой взгляд, получается сложно. Да, можно так написать, даже будет работать. Но качество кода будет выше, если Ripper не будет экономить оперативку и будет считать свои изображения immutable. Если он собирается применять к изображению фильтр, то скорее всего все равно будет обрабатывать каждый пиксел кадра. И затраты по времени будут те же самые, как если этот кадр сохранять в то же самое место. Зато можно будет обойтись простым shared_ptr. Заодно все методы работы с этим изображением станут thread safe, так как они будут константными.
Зато, обладая единственной в мире подводной орбитальной группировкой спутников глонасс...
gentoo.gif

#6 St.ALKer Опубликовано 03 Апрель 2012 - 7:48

St.ALKer
  • Свои
  • 59 Сообщений:
  • .

На мой взгляд, получается сложно.

As we all know, the First Amendment to the C++ Standard states: "The committee shall make no rule that prevents C++ programmers from shooting themselves in the foot." :)
отсюда: http://thbecker.net/...section_04.html

Ну и да, зависит от типичного размера изображения и области применения всего этого чуда.

Ещё по теме: исходная задача неуловимо напоминает http://en.wikipedia....ers–writer_lock.

Изменено: St.ALKer, 03 Апрель 2012 - 8:23


#7 KiberGus Опубликовано 03 Апрель 2012 - 8:26

KiberGus
  • Genius loci
  • 6 561 Сообщений:
  • Алексей Гусейнов

As we all know, the First Amendment to the C++ Standard states: "The committee shall make no rule that prevents C++ programmers from shooting themselves in the foot." :)
отсюда: http://thbecker.net/...section_04.html

Ну и да, зависит от типичного размера изображения и области применения всего этого чуда.

Конечно. Я знаю, что речь идет о потоковом наложении эффектов на видео. Но что-то мне подсказывает, что в данном случае Паел хочет сделать преждевременную оптимизацию.

Кстати, есть еще очень любопытный момент: синхронизация потоков. Синхронно ли работают различные этапы конвеера. Если нет, то как делается синхронизация на слиянии. Если да, то может быть вообще можно применить классический вариант с двойной буферизацией.
Зато, обладая единственной в мире подводной орбитальной группировкой спутников глонасс...
gentoo.gif

#8 Ripper Опубликовано 03 Апрель 2012 - 13:47

Ripper
  • Свои
  • 497 Сообщений:
  • Павел Рустанович

Насколько я понимаю ты говоришь о чём-то типа copy on write ( http://en.wikibooks....s/Copy-on-write ), который по слухам применяется в некоторых реализациях std::string.

Именно об этом. Пример по ссылке проверяет количество ссылок перед копированием, так что именно это я и искал.
Единственно, что выглядит подозрительно - это то, что одна Корова CowPtr всегда связан с собственным состоянием объекта, то есть чтобы из двух мест работать с одним состоянием, нужно ссылаться на один CowPtr. Подозрительным это кажется потому, что теоретически когда-нибудь может возникнуть необходимость сделать shared_ptr<CowPtr<...> > :wacko:

На мой взгляд, получается сложно. Да, можно так написать, даже будет работать.
Но качество кода будет выше, если Ripper не будет экономить оперативку и будет считать свои изображения immutable. Если он собирается применять к изображению фильтр, то скорее всего все равно будет обрабатывать каждый пиксел кадра. И затраты по времени будут те же самые, как если этот кадр сохранять в то же самое место. Зато можно будет обойтись простым shared_ptr.
Заодно все методы работы с этим изображением станут thread safe, так как они будут константными.

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

преждевременную оптимизацию.

Пожалуй, да. Просто оптимизированный вариант придумался раньше тривиального.

Кстати, есть еще очень любопытный момент: синхронизация потоков. Синхронно ли работают различные этапы конвеера. Если нет, то как делается синхронизация на слиянии. Если да, то может быть вообще можно применить классический вариант с двойной буферизацией.

Работают асинхронно. Между фильтрами буфер вроде посоветованного тут (http://304.ru/index....ndpost&p=181227).
Единственное многопоточное место - этот буфер.

post-2756-0-28658900-1472313042.png


#9 Ripper Опубликовано 03 Апрель 2012 - 13:59

Ripper
  • Свои
  • 497 Сообщений:
  • Павел Рустанович

Но только ты ещё хочешь контролировать количество клиентов. В таком случае, если есть код самого объекта, ты можешь встроить счётчик ссылок туда (intrusive reference counter, http://en.wikibooks....ms/Counted_Body ), из мутаторов возвращать новый указатель, а решать копировать ли объект будет код мутатора - если refcount == 1, то мутировать in-place, если > 1 - клонировать и вызвать тот же метод. Тут конечно плохо, что надо не забыть сохрнить возвращённый указатель.

Насколько я понял, это лучше CowPtr только меньшим числом задействованных объектов (shared_ptr, CowPtr, Объект - против - intrusive_ptr, Объект).

Изменено: Ripper, 03 Апрель 2012 - 14:00

post-2756-0-28658900-1472313042.png


#10 KiberGus Опубликовано 03 Апрель 2012 - 14:00

KiberGus
  • Genius loci
  • 6 561 Сообщений:
  • Алексей Гусейнов

Именно об этом. Пример по ссылке проверяет количество ссылок перед копированием, так что именно это я и искал.
что теоретически когда-нибудь может возникнуть необходимость сделать shared_ptr<CowPtr<...> > :wacko:

Просто не надо хрранить умные указатели в умных указателях.

Кроме того, даже если фильтр обрабатывает кадр попиксельно, оверхед по оперативке, наверное, может существенно сказаться на производительности.

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

Работают асинхронно. Между фильтрами буфер вроде посоветованного тут (http://304.ru/index....ndpost&p=181227).
Единственное многопоточное место - этот буфер.

То есть тебя устроит, если один поток идет с камеры с 25 fps, а другой читается с диска с той скоростью, с которой машина может, а на выходе ты их смешиваешь и либо имеешь огромный плавающий fps не синхронизированных потоков, либо у тебя неограниченно растет буфер неотправленных кадров и забивает всю оперативку.
Должна быть какая-то обратная связь, не позволяющая пропихнуть в конвеер очередной кадр, если он туда не лезет. Сделать такую можно, например, с помощью std::condition_variable. И тогда, в простейшем случае, ты можешь обойтись двумя буферами на каждую связь: в один пишем, из другого читаем. Когда данные прочитаны, а новые записаны, меняем буферы местами. Получаем фиксированное потребление памяти.
Ну или там может быть более ёмкий буфер, позволяющий накапливать несколько кадров. Посмоти на boost::circular_buffer.
Зато, обладая единственной в мире подводной орбитальной группировкой спутников глонасс...
gentoo.gif

#11 Ripper Опубликовано 03 Апрель 2012 - 14:24

Ripper
  • Свои
  • 497 Сообщений:
  • Павел Рустанович

Просто не надо хрранить умные указатели в умных указателях.

Я вот и боюсь, что необходомость может возникнуть.

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

Будет в два раза больше обращений к памяти (в кэше нужно будет хранить больше данных на операцию, кусок кадра, помещающийся в кэше станет меньше). А список пустых буферов я уже сам придумал :) Буду пользоваться кастомным удалятором в shared_ptr'е.


То есть тебя устроит, если один поток идет с камеры с 25 fps, а другой читается с диска с той скоростью, с которой машина может, а на выходе ты их смешиваешь и либо имеешь огромный плавающий fps не синхронизированных потоков, либо у тебя неограниченно растет буфер неотправленных кадров и забивает всю оперативку.
Должна быть какая-то обратная связь, не позволяющая пропихнуть в конвеер очередной кадр, если он туда не лезет.

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

Посмоти на boost::circular_buffer.

Уже :) Я его и собирался использовать.

Изменено: Ripper, 03 Апрель 2012 - 14:25

post-2756-0-28658900-1472313042.png


#12 KiberGus Опубликовано 03 Апрель 2012 - 14:42

KiberGus
  • Genius loci
  • 6 561 Сообщений:
  • Алексей Гусейнов

Я вот и боюсь, что необходомость может возникнуть.

У тебя объект скопировать быстрее, чем таскать умный указатель на него. Значит ты ничего не выиграешь по производительности.
У тебя указатель и так умный, значит ты ничего не выиграешь по функционалу.

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

Обращений к памяти будет одинаковое количество. У тебя фильтр сколько раз читает один пиксел из кадра? Если один, то кэш тебе никак не поможет. А если несколько раз (как например для blur'а), то ты не можешь писать обработанные данные поверх необработанных.

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

Главное глобальных часов не делай и текущее системное время не используй. А то ни протестировать не сможешь, ни в offline режиме запустить.
Зато, обладая единственной в мире подводной орбитальной группировкой спутников глонасс...
gentoo.gif

#13 Ripper Опубликовано 03 Апрель 2012 - 16:20

Ripper
  • Свои
  • 497 Сообщений:
  • Павел Рустанович

У тебя указатель и так умный, значит ты ничего не выиграешь по функционалу.

одна Корова CowPtr всегда связан с собственным состоянием объекта, то есть чтобы из двух мест работать с одним состоянием, нужно ссылаться на один CowPtr.

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

Изменено: Ripper, 03 Апрель 2012 - 16:20

post-2756-0-28658900-1472313042.png


#14 KiberGus Опубликовано 03 Апрель 2012 - 18:34

KiberGus
  • Genius loci
  • 6 561 Сообщений:
  • Алексей Гусейнов

CowPtr не наследует возможности shared_ptr делать несколько указателей (полноценных, не const) на один объект. Потенциальный выигрыш именно в этом.

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

Внутри одного потока, в методы обработки, передавай вообще ссылку. Им же не надо владеть объектом.

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

P.S. А знаетели вы, что в C++03
{
scoped_lock(mutex);
var = 10;
}
компилятор имеет право сначала разблокировать мьютекс, а потом записать 10 с регистра в память. Правда разработчики компиляторов таких идиотских оптимизаций стараются не делать.
Зато, обладая единственной в мире подводной орбитальной группировкой спутников глонасс...
gentoo.gif




1 пользователей читают эту тему

0 пользователей, 1 гостей, 0 невидимых