__sync_bool_compare_and_swap 在 Cython 中使用不同的参数类型
__sync_bool_compare_and_swap with different parameter types in Cython
我正在使用 Cython 快速并行处理数据,从多个线程向共享内存链表添加项目。我使用 __sync_bool_compare_and_swap,它提供原子比较和交换 (CAS) 操作来比较值是否未被修改(由另一个线程),然后再用新值替换它。
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* 参数?
编辑
我想到的是两个独立的原子操作,一个原子操作我想更新链表,另一个我想更新大小。你可以在 C 中做到这一点,但对于 Cython 这意味着你必须使用不同的参数引用同一个外部函数两次,可以做到吗?
结论
DavidW 建议的答案有效。如果有人想使用类似的结构,你应该知道,当使用两个单独的更新函数时,不能保证它们按顺序处理(即另一个线程可以在两者之间更新),但是,如果 objective是更新一个累积值,例如在多线程时监视进度,或者创建一个在所有线程完成之前不使用的聚合结果,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
所以问题(据我所知)是它 __sync_bool_compare_and_swap
是一个编译器内在函数而不是一个函数,所以实际上没有固定的签名,因为编译器只是想出来。然而,Cython 要求知道类型,并且因为你想将它用于两种不同的类型,所以你遇到了问题。
我看不出比使用(非常)少量的 C 来 "help" Cython 更简单的方法。用一堆 #defines
创建一个 header 文件
/* compare_swap.h */
#define sync_bool_compare_swap_voidp __sync_bool_compare_and_swap
#define sync_bool_compare_swap_int __sync_bool_compare_and_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 预处理器将生成您想要的代码,一切正常。
编辑:几年后看这个,我可以看到一些你可能会使用的更简单的方法(未经测试,但我不明白为什么他们不应该工作)。首先,您可以使用 Cython 将名称映射到 "cname" 的能力,以避免需要额外的 header:
cdef extern from *:
int sync_bool_compare_swap_voidp(void**, void*, void*) "__sync_bool_compare_and_swap"
int sync_bool_compare_swap_int(int*, int, int) "__sync_bool_compare_and_swap"
其次(可能是最好的)你可以使用一个通用定义(只是告诉 Cython 它是一个可变参数函数):
cdef extern from "compare_swap.h":
int __sync_bool_compare_and_swap(...)
这样 Cython 就不会试图理解所使用的类型,而只会将其完全推迟到 C(这正是您想要的)。
我不想评论您使用这样的两个原子操作是否安全,或者这是否会通过具有危险的不一致数据的状态....
我正在使用 Cython 快速并行处理数据,从多个线程向共享内存链表添加项目。我使用 __sync_bool_compare_and_swap,它提供原子比较和交换 (CAS) 操作来比较值是否未被修改(由另一个线程),然后再用新值替换它。
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* 参数?
编辑
我想到的是两个独立的原子操作,一个原子操作我想更新链表,另一个我想更新大小。你可以在 C 中做到这一点,但对于 Cython 这意味着你必须使用不同的参数引用同一个外部函数两次,可以做到吗?
结论
DavidW 建议的答案有效。如果有人想使用类似的结构,你应该知道,当使用两个单独的更新函数时,不能保证它们按顺序处理(即另一个线程可以在两者之间更新),但是,如果 objective是更新一个累积值,例如在多线程时监视进度,或者创建一个在所有线程完成之前不使用的聚合结果,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
所以问题(据我所知)是它 __sync_bool_compare_and_swap
是一个编译器内在函数而不是一个函数,所以实际上没有固定的签名,因为编译器只是想出来。然而,Cython 要求知道类型,并且因为你想将它用于两种不同的类型,所以你遇到了问题。
我看不出比使用(非常)少量的 C 来 "help" Cython 更简单的方法。用一堆 #defines
/* compare_swap.h */
#define sync_bool_compare_swap_voidp __sync_bool_compare_and_swap
#define sync_bool_compare_swap_int __sync_bool_compare_and_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 预处理器将生成您想要的代码,一切正常。
编辑:几年后看这个,我可以看到一些你可能会使用的更简单的方法(未经测试,但我不明白为什么他们不应该工作)。首先,您可以使用 Cython 将名称映射到 "cname" 的能力,以避免需要额外的 header:
cdef extern from *:
int sync_bool_compare_swap_voidp(void**, void*, void*) "__sync_bool_compare_and_swap"
int sync_bool_compare_swap_int(int*, int, int) "__sync_bool_compare_and_swap"
其次(可能是最好的)你可以使用一个通用定义(只是告诉 Cython 它是一个可变参数函数):
cdef extern from "compare_swap.h":
int __sync_bool_compare_and_swap(...)
这样 Cython 就不会试图理解所使用的类型,而只会将其完全推迟到 C(这正是您想要的)。
我不想评论您使用这样的两个原子操作是否安全,或者这是否会通过具有危险的不一致数据的状态....