2016-05-12 3 views
3

Я использую Cython для быстрой параллельной обработки данных, добавляя элементы в список связанных с общей памятью из нескольких потоков. Я использую __sync_bool_compare_and_swap, который предоставляет операцию атомного сравнения и свопа (CAS) для сравнения, если значение не было изменено (другим потоком), прежде чем заменять его новым значением.__sync_bool_compare_and_swap с различными типами параметров в Cython

cdef extern int __sync_bool_compare_and_swap (void **ptr, void *oldval, void *newval) nogil 

cdef bint firstAttempt = 1 
cdef type *next = NULL 
cdef type *newlink = .... 

while firstAttempt or not __sync_bool_compare_and_swap(<void**> c, <void*>next, <void*>newlink): 
    firstAttempt = 0 
    next = c[0] 
    newlink.next = next 

Это работает очень хорошо. Однако теперь я также хочу отслеживать размер связанного списка и хочу использовать одну и ту же функцию CAS для обновлений, однако на этот раз это не указатели, которые необходимо обновить, а int. Как можно использовать одну и ту же внешнюю функцию дважды в Cython, один раз с параметром void ** и один раз с параметром int *?

EDIT

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

ЗАКЛЮЧЕНИЕ

Ответ предложил DavidW работы. В случае, если кто-то думает использовать аналогичную конструкцию, вы должны знать, что при использовании двух отдельных функций обновления нет гарантии, что они обрабатываются последовательно (т. Е. Другой поток может обновляться между ними), однако, если целью является обновление кумулятивное значение, например, для отслеживания прогресса при многопоточности или для создания агрегированного результата, который не используется до тех пор, пока все потоки не будут завершены, CAS гарантирует, что все обновления выполняются ровно один раз. Неожиданно gcc отказывается компилировать без кастинга в void *, поэтому либо определите отдельные жестко типизированные версии, либо вам нужно выполнить бросок. Отрывок из моего кода:

в some_header.h:

#define sync_bool_compare_and_swap_int __sync_bool_compare_and_swap 
#define sync_bool_compare_and_swap_vp __sync_bool_compare_and_swap 

в some_prog.pxd:

cdef extern from "some_header.h": 
    cdef extern int sync_bool_compare_and_swap_vp (void **ptr, void *oldval, void *newval) nogil 
    cdef extern int sync_bool_compare_and_swap_int (int *ptr, int oldval, int newval) nogil 

в some_prog.pyx:

cdef void updateInt(int *value, int incr) nogil: 
    cdef cINT previous = value[0] 
    cdef cINT updated = previous + incr 

    while not sync_bool_compare_and_swap_int(c, previous, updated): 
     previous = value[0] 
     updated = previous + incr 
+0

Это обычная невозможно обойтись без блокировки, если вы хотите, чтобы данные были согласованными. что связано с атомарным изменением данных в двух совершенно разных местах памяти. Похоже, было бы полезно, хотя бы. Каков ваш фактический прецедент? – Voo

+0

@Voo добавила некоторую ясность в вопрос, это могут быть две отдельные атомные операции, проблема в том, как дважды ссылаться на одну и ту же внешнюю функцию в Cython с разными параметрами? –

+0

Достаточно честно, проблема в том, что на самом деле это не «функции». Они встроенные, которые ведут себя подобно функции. Я вижу только два очевидных обходных пути: свяжите c dll, которая пересылает вызовы или помещает определения в отдельные модули. – Voo

ответ

3

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

Я не вижу более простого способа, чем прибегать к (очень) небольшому количеству C, чтобы «помочь» Китону. Создайте файл заголовка с кучей #defines

/* compare_swap.h */ 
#define sync_bool_compare_swap_voidp __sync_bool_compare_swap 
#define sync_bool_compare_swap_int __sync_bool_compare_swap 

Вы можете сказать Cython, что каждый из них является отдельной функцией

cdef extern from "compare_swap.h": 
    int sync_bool_compare_swap_voidp(void**, void*, void*) 
    int sync_bool_compare_swap_int(int*, int, int) 

На этом этапе вы должны быть в состоянии использовать их, естественно, как и обычные функции без какого-либо типа литья (т. е. <void**> в вашем коде, поскольку это имеет тенденцию скрывать реальные ошибки). Препроцессор C будет генерировать код, который вы хотите, и все хорошо.


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

+0

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

+0

@jeroen стоимость атомных операций, вероятно, будет выше, чем повторение одного раза через список после того, как вы закончите. – Voo

Смежные вопросы