「Python数据科学」对比Python的对象内存结构,分析NumPy的高性能

南宫理的日志录 2024-12-17 08:41:12
引言

Python中的列表类型很是灵活,可以存储不同类型的元素,这种灵活性来自于Python动态类型的特性,以及“一切皆对象”的设计理念。

相较于Python列表的灵活性,NumPy中的ndarray仅支持存储同种类型的元素,通常来说,更多的是整型和浮点型数字,但是性能却得到了极大的提升。

本文尝试辩证地分析Python中的动态类型特性和NumPy数组的高性能,从而看到不同设计理念及其针对的适用场景。

本文的主要内容有:

1、Python的对象结构

2、C数组、NumPy数组及Python列表的存储结构

3、通过代码验证NumPy数组的高性能

Python的对象结构

在C语言中,我们声明每个变量时,都需要首先声明其类型。Python作为一种动态类型的编程语言,在进行变量声明时,无须显式指定变量类型,Python中的类型是动态推断的。

这种动态推断的特性,是Python和其他动态类型的语言更加易用的原因之一。但是,这种类型灵活性也指出了一个事实:Python中的变量不只是一个简单的变量值。

在Python中,一切皆对象,即便是一个简单的整数,在内存中也有一个完整的对象结构。概括来说,一个Python对象的内存结构至少要包括以下几个部分,从而来支持动态类型的灵活性和自动内存管理(自动垃圾回收)的便捷性:

1、对象头信息(PyObject_HEAD)

不同版本的CPython实现可能有些细微差别,但是,通常对象头信息中会包含引用计数(ob_refcnt)和类型指针(ob_type)这两个关键信息,分别用于记录当前该对象的引用数,以及对象的类型信息。

2、对象数据(Object Data)

对象的实际数据存储部分,这部分的结构和内容依赖于对象的具体类型。比如,一个整型对象,可能只存储一个整数值;而一个列表对象,这会包含一个指向所有元素的动态数组的指针。

当然,除了这两块比较核心的信息,还会有一些其他的对象属性,比如列表的长度,多维数组的维度等。

下面,通过C和Python中定义整型变量,来简单示意其不同的内存结构

C数组、NumPy数组及Python列表的存储结构

在数据科学中,批量的数据计算是很常见的,因而多维数组是一个最常用的存储结构。而且,数据科学中,整型和浮点型的数值计算居多。所以,NumPy数组被设计为只能存储相同类型的元素是合理的。

Python中的列表能够灵活存储不同类型的元素,更多的是针对C语言数组中只能存储同类型元素的一个灵活性上的提升。但是,编程语言层面上的设计,所针对的是通用性、灵活性的考虑。因而,与聚焦于数据科学的NumPy中的多维数组设计上是不矛盾的。

下面通过C、NumPy数组和Python列表的简单示意图,进一步看下各自在存储开销及执行计算上的异同。

从上面的示意图中,可以看出:

1、C语言中数组的存储开销最小,而且所有元素按照顺序在内存中连续存储。

2、NumPy数组,首先是一个Python的对象,对象头信息是需要单独存储的,其数组元素也是按照顺序在内存中连续存储的。

3、Python列表,列表本身是一个Python对象,列表中的每个元素也是Python对象。列表对象的数据元素中,存储的是每个元素对象的引用或者指针,每个元素指针又再次指向元素对象。列表中的元素是离散存储的。

前面已经提及,Python中一切皆对象的设计,以及动态类型的支持,采用这种设计是合理的,为了实现灵活性和使用便捷性,在性能上做出了让步与妥协。

C语言中的数组,以及NumPy中的数组,所有的元素是相同类型的,且是连续存储的,存储开销降低的同时,在元素数据的寻址、计算上也会带来极大的提升。

通过代码验证NumPy数组的高性能

接下来,我们通过代码来简单比较下NumPy数组与Python列表的计算性能,直接看代码:

同样是随机生成一个100000000的数组,然后对这个数组的所有元素进行求和。使用Python中的列表和NumPy数组相比,性能上有10倍的差距。

需要补充说明的是:

在Jupyter Notebook中,可以通过在一行代码前加上%time或者%timeit来进行执行耗时的统计。其中:

%time只执行一次,并给出执行耗时。

%timeit会执行多次,比如演示的程序中,分别执行了7次,来计算平均的耗时。

当然,ipython中也是支持这种用法的,感兴趣的可以自行尝试。

虽然不是一个全面的比较,但是,基于两者的内存结构的分析,以及简单的元素求和的计算,相信能对NumPy多维数组在科学计算上做了足够多的优化,有一个大致的感受。

总结

本文简单介绍了Python中对象的内存结构,对比了C语言数组、NumPy数组,以及Python列表存储相同数组的情况下的内存结构,从内存结构的差异上,可以看出Python为了支持灵活性和易用性,在性能上所作出的妥协。最后,通过代码简单比较了NumPy和Python列表的计算性能。

感谢您的拨冗阅读,希望对您有所帮助!

0 阅读:2

南宫理的日志录

简介:深耕IT科技,探索技术与人文的交集