Полностью ориентированная на многопотоковое исполнение shared_ptr реализация

Мы можем вывести функцию, заключив ее в list

library(tidyverse)
df %>% 
    group_by(group) %>%
    summarise(fns = list(splinefun(x, y)))

Если предполагается создать столбец в исходных данных, заменить summarise на mutate. Однако это будет повторять функцию для каждой строки группы


Если мы хотим применить функции к другому набору данных в соответствующей «группе», то выполнить объединение со вторым набором данных и применить функция сохраняется в первом столбце данных в интересующем столбце вторых данных с использованием map2

df %>% 
   group_by(group) %>% 
   summarise(fns = list(splinefun(x, y))) %>% 
   left_join(df2) %>% 
   mutate(newval = map2_dbl(fns, xval, ~ .x(.y)))

Обновление

На основе обновленных данных в сообщении ОП

fit %>% 
  right_join(newData) %>% 
  mutate(newval = map2_dbl(fns, xval, ~ if(!is.null(.x)) .x(.y) else NA_real_))

Или создайте функцию с possibly (из purrr) и примените

f1 <- possibly(function(x, y) x(y), otherwise = NA_real_)
fit %>% 
  right_join(newData) %>% 
  mutate(newval = map2_dbl(fns, xval, f1))

данные

df2 <- data.frame(group = 1:3, xval = c(1.5, 2.5, 3.2))
newData <- data.frame(group=c(rep(1,5),rep(2,5),rep(3,5),rep(4,5)), 
                   xval = rep(c(0,1,2,3,4),4))
13
задан Piotr Dobrogost 6 July 2009 в 16:34
поделиться

7 ответов

Добавление необходимых барьеров для такой полностью поточно-безопасной реализации shared_ptr, вероятно, повлияет на производительность. Рассмотрим следующую расу (примечание: псевдокод имеется в большом количестве):

Тема 1: global_ptr = A;

Тема 2: global_ptr = B;

Тема 3: local_ptr = global_ptr;

Если мы разделим это на составляющие операции:

Поток 1:

A.refcnt++;
tmp_ptr = exchange(global_ptr, A);
if (!--tmp_ptr.refcnt) delete tmp_ptr;

Поток 2:

B.refcnt++;
tmp_ptr = exchange(global_ptr, B);
if (!--tmp_ptr.refcnt) delete tmp_ptr;

Поток 3:

local_ptr = global_ptr;
local_ptr.refcnt++;

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

Чтобы справиться с этим, нам нужно использовать фиктивное значение, пока поток 3 выполняет обновление refcnt: (примечание: compare_exchange (переменная, ожидаемое, новое) атомарно заменяет значение в переменной новым, если оно в настоящее время равно new, затем возвращает истину, если это было сделано успешно)

Поток 1:

A.refcnt++;
tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
    tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;

Поток 2:

B.refcnt++;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
    tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;

Поток 3:

tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, BAD_PTR))
    tmp_ptr = global_ptr;
local_ptr = tmp_ptr;
local_ptr.refcnt++;
global_ptr = tmp_ptr;

Теперь вам нужно добавить цикл с атомами в середине вашей / read / операции. Это нехорошо - для некоторых процессоров это может быть очень дорого. Более того, вы тоже заняты-ждете. Вы можете начать изобретать фьютексы и тому подобное, но к этому моменту вы заново изобрели блокировку.

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

18
ответ дан 2 December 2019 в 00:32
поделиться

Одновременная запись к встроенному указателю, конечно, не ориентирована на многопотоковое исполнение. Рассмотрите последствия записи в то же значение относительно барьеров памяти, если Вы действительно хотите свести себя с ума (например, у Вас могло бы быть два потока, думая, что тот же указатель имел различные значения).

РЕ: Комментарий - созданная-ins причина не удваивает удаление, то, потому что они не удаляют вообще (и реализация повышения:: shared_ptr, который я использую, не удвоился бы, удаляют, так как он использует специальный атомарный инкремент и декремент, таким образом, он был бы только единственный удалять, но затем результат был бы, мог иметь указатель от одного и касательно количества другого. Или в значительной степени любая комбинация двух. Это было бы плохо.). Оператор в документах повышения корректен, как это, Вы получаете те же гарантии, как Вы делаете со встроенным.

РЕ: EDIT2 - первая ситуация, которую Вы описываете, очень отличается между созданным-ins использованием и shared_ptrs. В одном (XCHG и руководство удаляют) нет никакого подсчета ссылок; Вы предполагаете, что Вы - тот и только владелец, когда Вы делаете это. Если использование совместно использовало указатели, Вы говорите, что другие потоки могли бы иметь владение, которое делает вещи намного более сложными. Я полагаю, что это возможно со сравнивать-и-подкачивать, но это было бы очень непортативно.

C++ 0x выпускает библиотеку атомной энергетики, которая должна сделать намного легче написать универсальный многопоточный код. Необходимо будет, вероятно, ожидать, пока это не выходит для наблюдения хороших межплатформенных ссылочных реализаций ориентированных на многопотоковое исполнение интеллектуальных указателей.

2
ответ дан 2 December 2019 в 00:32
поделиться

Ваш компилятор может уже обеспечить ориентированные на многопотоковое исполнение интеллектуальные указатели в более новых Стандартах C++. Я полагаю, что TBB является планированием добавления интеллектуального указателя, но я не думаю, что это было включено все же. Вы можете использовать один из ориентированных на многопотоковое исполнение контейнеров TBB, все же.

-1
ответ дан 2 December 2019 в 00:32
поделиться

Я не знаю о такой реализации интеллектуального указателя, хотя я должен спросить: как это поведение могло быть полезным? Единственные сценарии, о которых я могу думать, где Вы нашли бы одновременные обновления указателя, являются условиями состязания (т.е. ошибки).

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

Ре: EDIT2 спасибо за обеспечение нескольких сценариев. Это действительно кажется, что атомарные записи указателя были бы полезны в тех ситуациях. (Одна небольшая вещь: для второго примера, когда Вы записали, "Если это не является ПУСТЫМ, это препятствует тому, чтобы процессор был уничтожен путем присвоения его его собственному общему-ptr", я надеюсь, что Вы подразумевали присвоение глобального общего указателя на локальный общий указатель, сначала затем проверяют, является ли локальный общий указатель НУЛЕВЫМ - способ, которым Вы описали это, подвержено состоянию состязания, где глобальный общий указатель становится ПУСТЫМ после тестирования на него и перед присвоением его локальному.)

1
ответ дан 2 December 2019 в 00:32
поделиться

You can easily do this by including a mutex object with every shared pointer, and wrapping increment/decrement commands with the lock.

-1
ответ дан 2 December 2019 в 00:32
поделиться

Я не думаю, что это так просто, недостаточно обернуть ваши классы sh_ptr с помощью CS. Это правда, что если вы поддерживаете одну единую CS для всех общих указателей, это может гарантировать избежание взаимного доступа и удаления объектов sh_ptr между разными потоками. Но это было бы ужасно, ведь один объект CS для каждого общего указателя был бы настоящим узким местом. Было бы удобно, если бы каждый оборачиваемый новый ptr -s имел разные CS, но таким образом мы должны создавать нашу CS динамически и обеспечивать копирование ctors классов sh_ptr для передачи этих общих C. Теперь мы подошли к той же проблеме: кто гарантирует, что этот Cs ptr уже удален или нет. Мы можем быть немного более умными с изменчивыми флагами m_bReleased для каждого экземпляра, но таким образом мы не сможем устранить пробелы в безопасности между проверкой флага и использованием общих Cs. Я могу' Я не вижу полностью безопасного решения этой проблемы. Возможно, этот ужасный глобальный C будет второстепенным недостатком, убивающим приложение. (извините за мой английский)

-1
ответ дан 2 December 2019 в 00:32
поделиться

Вы можете использовать эту реализацию Atomic Reference Counting Pointers, чтобы по крайней мере реализовать механизм подсчета ссылок.

0
ответ дан 2 December 2019 в 00:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: