在cython

时间:2018-01-18 11:36:40

标签: python numpy struct cython

我想在cython中使用像structarray这样的东西,我希望这个structarray在python中像在cython中一样容易访问。 基于一时兴起,我使用了一个使用dtype的recarray,看起来像我想要使用的结构。奇怪的是,它只是工作,并允许我使用一个c structarray,通过引擎盖;),是python用户的numpy recarray。

以下是我的示例

# This is a "structarray in cython with numpy recarrays" testfile
import numpy as np
cimport numpy as np

# My structarray has nodes with fields x and y
# This also works without packed, but I have seen packed used in other places where people asked similar questions
# I assume that for two doubles that is equivalent but is necessary for in8s in between
cdef packed struct node:
    double x
    double y
# I suppose that would be the equivalent numpy dtype?
# Note: During compilation it warns me about double to float downcasts, but I do not see where
nodetype = [('x' , np.float64),('y', np.float64)]

def fun():
    # Make 10 element recarray
    # (Just looked it up. A point where 1-based indexing would save a look in the docs)
    mynode1 = np.recarray(10,dtype=nodetype)

    # Recarray with cdef struct
    mynode1 = np.recarray(10,dtype=nodetype)

    # Fill it with non-garbage somewhere
    mynode1[2].x=1.0
    mynode1[2].y=2.0

    # Brave: give recarray element to a c function assuming its equivalent to the struct
    ny = cfuny(mynode1[2])
    assert ny==2.0 # works!

    # Test memoryview, assuming type node
    cdef node [:] nview = mynode1
    ny = cfunyv(nview,2)
    assert ny==2.0 # works!

    # This sets the numpy recarray value with a c function the gts a memoryview
    cfunyv_set(nview,5,9.0)
    assert mynode1[5].y==9.0 # alsow works!

    return 0

# return node element y from c struct node
cdef double cfuny(node n):
    return n.y

# give element i from memoryview of recarray to c function expecting a c struct
cdef double cfunyv(node [:] n, int i):
    return cfuny(n[i])

# write into recarray with a function expecting a memoryview with type node
cdef int cfunyv_set(node [:] n,int i,double val):
    n[i].y = val
    return 0

当然,我不是第一个尝试这个的人。

Here例如同样的事情已经完成,它甚至声明这种用法将成为手册here的一部分,但我无法在页面上找到它。我怀疑它在某个时刻存在。还有一些涉及在这种自定义类型中使用字符串的讨论(例如here),从我收集的答案中,在cstruct上进行重组的可能性是预期的行为 ,讨论中讨论了关于给定示例的回归测试并在某个时刻修复了字符串错误。

我的问题

我找不到任何文档说明除了论坛答案之外这应该有用。有人能告诉我记录的位置吗?

并且,为了一些额外的好奇心

  • 在numpy或cython的开发过程中,这可能会在任何时候破裂吗?
  • 从主题上的其他论坛条目来看,似乎 packed 是必要的,因为一旦有趣的数据类型是结构的一部分,它就可以工作。我不是编译器专家,我从未使用过结构打包,但我怀疑结构是否打包取决于编译器设置。这是否意味着在没有打包结构的情况下编译numpy的人需要在没有打包的情况下编译此cython代码?

2 个答案:

答案 0 :(得分:2)

这似乎没有直接记录。我可以给你的最佳参考是这里输入的内存视图docs

而不是对numpy结构化dtypes的特定cython支持,而不是支持PEP 3118缓冲区协议的结果。 numpy为其数组公开了一个Py_buffer结构,cython知道如何将它们转换为结构。

包装是必要的。我的understanding是x86在itemsize字节边界上对齐,而numpy结构化dtype被打包到尽可能小的空间。可能最明显的例子是:

%%cython
import numpy as np

cdef struct Thing:
    char a
    # 7 bytes padding, double must be 8 byte aligned
    double b

thing_dtype = np.dtype([('a', np.byte), ('b', np.double)])
print('dtype size: ', thing_dtype.itemsize)
print('unpacked struct size', sizeof(Thing))
dtype size:  9
unpacked struct size 16

答案 1 :(得分:1)

回答最后一个子问题:

  

从主题的其他论坛条目来看,似乎打包是必要的,一旦更多有趣的数据类型是结构的一部分,它就可以工作。我不是编译器专家,我从未使用过结构打包,但我怀疑结构是否打包取决于编译器设置。这是否意味着在没有打包结构的情况下编译numpy的人需要在没有打包的情况下编译这个cython代码?

Numpy的行为是在运行时而不是编译时决定的。它将计算结构可能需要的最小空间量并分配其中的块。它不会被任何编译器设置更改,因此应该是可靠的。

因此总是需要

alignas来匹配numpy。但是,它不会生成符合标准的C代码。相反,它使用GCCMSVC(和其他)的扩展名。因此,它在当前存在的主要C编译器上工作正常,但原则上可能在将来的编译器上失败。看起来应该可以使用C11标准java.lang.NullPointerException at java.base/java.util.regex.Matcher.getTextLength(Matcher.java:1769) at java.base/java.util.regex.Matcher.reset(Matcher.java:416) at java.base/java.util.regex.Matcher.<init>(Matcher.java:253) at java.base/java.util.regex.Pattern.matcher(Pattern.java:1147) at java.base/java.util.regex.Pattern.split(Pattern.java:1264) at java.base/java.util.regex.Pattern.split(Pattern.java:1335) at sbt.IO$.pathSplit(IO.scala:797) at sbt.IO$.parseClasspath(IO.scala:912) at sbt.compiler.CompilerArguments.extClasspath(CompilerArguments.scala:6 6) at sbt.compiler.MixedAnalyzingCompiler$.withBootclasspath(MixedAnalyzing Compiler.scala:188) at sbt.compiler.MixedAnalyzingCompiler$.searchClasspathAndLookup(MixedAn alyzingCompiler.scala:166) at sbt.compiler.MixedAnalyzingCompiler$.apply(MixedAnalyzingCompiler.sca la:176) at sbt.compiler.IC$.incrementalCompile(IncrementalCompiler.scala:138) at sbt.Compiler$.compile(Compiler.scala:152) at sbt.Compiler$.compile(Compiler.scala:138) at sbt.Defaults$.sbt$Defaults$$compileIncrementalTaskImpl(Defaults.scala :860) at sbt.Defaults$$anonfun$compileIncrementalTask$1.apply(Defaults.scala:8 51) at sbt.Defaults$$anonfun$compileIncrementalTask$1.apply(Defaults.scala:8 49) at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47) at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40) at sbt.std.Transform$$anon$4.work(System.scala:63) at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:22 8) at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:22 8) at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17) at sbt.Execute.work(Execute.scala:237) at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228) at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228) at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestric tions.scala:159) at sbt.CompletionService$$anon$2.call(CompletionService.scala:28) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executo rs.java:514) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoo lExecutor.java:1167) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPo 以符合标准的方式实现相同的功能,因此如果需要,可以有希望修改Cython。