C桑(Cython)从入门到入土(2): np数组操作对比

发布时间:2024年01月12日

在这里插入图片描述

C桑(Cython)从入门到入土

2. np数组操作对比

Node sc518 Copy has image data of type unsigned long long

实际上, 👆🏻这个错误是我探索Cython的缘起…

code1:

Form:

更现代的方法是使用内存视图而不是指针:

cdef np.uint32_t[:,:,::1] mv_buff = np.ascontiguousarray(im, dtype = np.uint32)

[:,;,::1] 语法告诉 Cython 内存视图在内存中是 3D 和 C 连续的。将类型定义为内存视图而不是 numpy 数组的优点是
它可以接受定义缓冲区接口的任何类型,例如内置数组模块或来自 PIL 成像库的对象。
Memoryviews 可以在不持有 GIL 的情况下传递,这对于并行代码很有用
要从内存视图获取指针,请获取第一个元素的地址:

cdef np.uint32_t* im_buff = &mv_buff[0,0,0]

这比 <np.uint32_t*>mv_buff.data 更好,因为它避免了强制转换,并且强制转换通常可以隐藏错误。

👇的内容是小度在这里插入图片描述的回答:

这段代码涉及到Cython中的内存视图和numpy数组的使用。Cython是一种用来编写C扩展的Python语言,它可以提高Python代码的执行效率。让我们逐步解释这段代码:

  1. cdef np.uint32_t[:,:,::1] mv_buff = np.ascontiguousarray(im, dtype = np.uint32):

    • cdef np.uint32_t[:,:,::1] mv_buff: 这定义了一个3D的内存视图mv_buff,其数据类型为np.uint32_t(无符号32位整数)。:表示这个维度是开放的,也就是说它的大小可以根据源数组im的大小来自动确定。
    • np.ascontiguousarray(im, dtype = np.uint32): 这个函数将输入的numpy数组im转换为连续的内存布局,并指定数据类型为np.uint32_t
  2. [:,;,::1] 语法告诉 Cython 内存视图在内存中是 3D 和 C 连续的。:

    • [:,;,::1]是Cython中的内存视图语法,它定义了数组的维度和连续性。这里它表示一个3D的数组,并且在内存中是C风格连续的(即行优先)。
  3. 将类型定义为内存视图而不是 numpy 数组的优点是:

    • 可以接受定义缓冲区接口的任何类型,例如内置数组模块或来自 PIL 成像库的对象。
    • Memoryviews 可以在不持有 GIL 的情况下传递,这对于并行代码很有用。
  4. 要从内存视图获取指针,请获取第一个元素的地址:

    • cdef np.uint32_t* im_buff = &mv_buff[0,0,0]: 这行代码获取了内存视图mv_buff中第一个元素的地址,并将其赋给指针im_buff。这意味着现在可以通过这个指针直接访问和修改mv_buff中的数据。

简而言之,这段代码的主要目的是将一个numpy数组转换为连续的内存布局(使用Cython的内存视图),并获取该内存视图的第一个元素的地址。这样做通常是为了提高数据访问的效率或与需要连续内存布局的C/C++代码进行交互。

code2

From

from cython.cimports.cython.view import array as cvarray
import numpy as np

# Memoryview on a NumPy array
narr = np.arange(27, dtype=np.dtype("i")).reshape((3, 3, 3))
narr_view = cython.declare(cython.int[:, :, :], narr)

# Memoryview on a C array
carr = cython.declare(cython.int[3][3][3])
carr_view = cython.declare(cython.int[:, :, :], carr)

# Memoryview on a Cython array
cyarr = cvarray(shape=(3, 3, 3), itemsize=cython.sizeof(cython.int), format="i")
cyarr_view = cython.declare(cython.int[:, :, :], cyarr)

# Show the sum of all the arrays before altering it
print(f"NumPy sum of the NumPy array before assignments: {narr.sum()}")

# We can copy the values from one memoryview into another using a single
# statement, by either indexing with ... or (NumPy-style) with a colon.
carr_view[...] = narr_view
cyarr_view[:] = narr_view
# NumPy-style syntax for assigning a single value to all elements.
narr_view[:, :, :] = 3

# Just to distinguish the arrays
carr_view[0, 0, 0] = 100
cyarr_view[0, 0, 0] = 1000

# Assigning into the memoryview on the NumPy array alters the latter
print(f"NumPy sum of NumPy array after assignments: {narr.sum()}")

# A function using a memoryview does not usually need the GIL
@cython.nogil
@cython.ccall
def sum3d(arr: cython.int[:, :, :]) -> cython.int:
    i: cython.size_t
    j: cython.size_t
    k: cython.size_t
    I: cython.size_t
    J: cython.size_t
    K: cython.size_t
    total: cython.int = 0
    I = arr.shape[0]
    J = arr.shape[1]
    K = arr.shape[2]
    for i in range(I):
        for j in range(J):
            for k in range(K):
                total += arr[i, j, k]
    return total

# A function accepting a memoryview knows how to use a NumPy array,
# a C array, a Cython array...
print(f"Memoryview sum of NumPy array is {sum3d(narr)}")
print(f"Memoryview sum of C array is {sum3d(carr)}")
print(f"Memoryview sum of Cython array is {sum3d(cyarr)}")
# ... and of course, a memoryview.
print(f"Memoryview sum of C memoryview is {sum3d(carr_view)}")
# 此代码应提供以下输出:

NumPy sum of the NumPy array before assignments: 351
NumPy sum of NumPy array after assignments: 81
Memoryview sum of NumPy array is 81
Memoryview sum of C array is 451
Memoryview sum of Cython array is 1351
Memoryview sum of C memoryview is 451

👇的内容是小度在这里插入图片描述的回答:

这段代码主要是关于Cython的内存视图(memoryview)特性,以及如何使用Cython与NumPy、C数组和Cython数组进行交互。下面是对代码的详细解释:

  1. 导入相关模块:

    • from cython.cimports.cython.view import array as cvarray: 导入Cython的carray(C风格数组)作为cvarray。
    • import numpy as np: 导入NumPy库。
  2. 创建NumPy数组:

    • narr = np.arange(27, dtype=np.dtype("i")).reshape((3, 3, 3)): 创建一个形状为(3,3,3)的整数数组,并初始化其值。
  3. 声明Cython内存视图:

    • narr_view = cython.declare(cython.int[:, :, :], narr): 为NumPy数组创建一个Cython内存视图。
    • carr = cython.declare(cython.int[3][3][3]): 声明一个C风格的3x3x3整数数组。
    • cyarr = cvarray(shape=(3, 3, 3), itemsize=cython.sizeof(cython.int), format="i"): 创建一个Cython的carray。
  4. 显示数组和赋值:

    • print(f"NumPy sum of the NumPy array before assignments: {narr.sum()}"): 在赋值之前打印NumPy数组的总和。
    • carr_view[...] = narr_viewcyarr_view[:] = narr_view: 将NumPy数组的值复制到C风格数组和Cython数组中。
    • narr_view[:, :, :] = 3: 将NumPy数组的所有元素设置为3。
    • carr_view[0, 0, 0] = 100cyarr_view[0, 0, 0] = 1000: 分别设置C风格数组和Cython数组的特定元素值。
  5. 检查赋值后的总和:

    • print(f"NumPy sum of NumPy array after assignments: {narr.sum()}"): 在赋值之后打印NumPy数组的总和,由于所有元素都被设置为3,所以总和应为27。
  6. 定义一个使用内存视图的函数:

    • @cython.nogil@cython.ccall 是Cython的装饰器,它们允许函数在没有全局解释器锁(GIL)的情况下运行,并允许直接调用C函数。
    • sum3d 函数接受一个三维整数数组作为参数,并返回其所有元素的总和。
  7. 调用内存视图函数:
    使用先前定义的sum3d函数,分别对NumPy数组、C风格数组、Cython数组以及C风格的内存视图调用该函数,并打印结果。

总的来说,这段代码展示了如何使用Cython的内存视图来与NumPy、C风格数组和Cython数组进行交互,以及如何通过内存视图进行高效的数值计算。

小结

C桑 这种啰嗦的语言风格, 真是让Python🐶抓狂😖…

不过, 这种高效倒是真的…

我们现在比纯 Python 版本快 7558 倍,比 NumPy 快 11.1 倍!
以上
未完待续

文章来源:https://blog.csdn.net/sinat_21835983/article/details/135561076
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。