我的博客

python3 list 和 tuple 性能对比

目录
  1. 代码
    1. 代码一:tuple 版
    2. 代码二:list 版
    3. 代码三:list 版(使用同一个对象)
    4. 代码四
    5. 代码五
    6. 代码六
    7. 代码七
    8. 代码八

tuple 是不可变对象,理论上性能应该比 list 快。但是我在最近的次数据分析中发现,用 tuple 替换 list 反而导致内存和时间的消耗显著增多。所以我做了一个简单的实验。

定义一个 list,然后往 list 中 append 1 亿个 list 或者 tuple,其中的元素都是 1,2,3,4,5 五个数字。

1
2
3
4
5
6
7
8
9
$python3 test_tuple.py
100%|█████████████| 100000000/100000000 [00:23<00:00, 4185364.44it/s]

0.7727 GB

$ python3 test_list.py
100%|█████████████| 100000000/100000000 [01:14<00:00, 1339510.06it/s]

12.9193 GB

tuple 耗时 23 秒,使用内存 0.7727 GB (代码一)

list 耗时 74 秒,使用内存 12.9193 GB(代码二)

通过简单的读取 /proc/meminfo 获得大致的内存消耗,windows 无法运行,还需要安装依赖 tqdm(pip install tqdm

上面的实验是有问题的, 感谢 rainy 的评论,这里 tuple 比 list 快和省内存的原因是编译器优化了 tuple 的代码,每次 append 的 tuple 对象都是同一个,所以实际上这里只创建了一个对象。所以我添加了两个实验。

如 rainy 评论的,list 版在循环外定义一个 x = [1,2,3,4,5,] 每次 append(x), 得到的结果与 tuple 相同

耗时 24 秒,内存消耗 0.748 GB(代码三)

1
2
3
100%|██████████| 100000000/100000000 [00:24<00:00, 4051356.31it/s]

0.7480697631835938

所以说代码一的实际只创建了一个对象。

于是又做了一组实验,append 5 千万次,i 从 0 到 5 千万,每次 append 的是 [i+1, i+2, i+3, i+4, i+5,] 或者 (i+1, i+2, i+3, i+4, i+5,)。这样避免了解释器优化的问题。

1
2
3
4
5
6
7
8
9
10
11
tuple(代码四)

100%|██████████| 50000000/50000000 [00:27<00:00, 1825827.72it/s]

12.5092 GB

list(代码五)

100%|██████████| 50000000/50000000 [00:51<00:00, 970668.78it/s]

14.0284 GB

tuple 比 list 稍好一些

最后我仔细看了下之前的代码,复现了一下我当时遇到的问题。原来我当时是这样做的:[int(s, 16) for s in x], x 是一个数组,元素是 16 进制字符串。我误以为把 [] 换成 () 就从 list 换成 tuple 了,结果实际上

1
2
3
4
>>> [int(s, 16) for s in ['4f', 'e2']]
[79, 226]
>>> (int(s, 16) for s in ['4f', 'e2'])
<generator object <genexpr> at 0x00EFDB18>

换成 () 后得到的是 generator,换成 tuple(int(s, 16) for s in ['4f', 'e2']) 就好了。原来 generator 消耗这么大

实验结果:

1
2
3
4
5
6
7
8
9
10
11
tuple(代码六)
100%|██████████| 5000000/5000000 [00:36<00:00, 136155.14it/s]
1.5345726013183594

list (代码七)
100%|██████████| 5000000/5000000 [00:40<00:00, 124367.94it/s]
1.7481269836425781

generator(代码八,这个是无意中的错误,实际上不应该用 generator)
100%|██████████| 5000000/5000000 [00:53<00:00, 93451.91it/s]
5.271724700927734

代码

代码一:tuple 版

1
2
3
4
5
6
7
8
9
10
11
12
13
from tqdm import tqdm
def get_free_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])
f1 = get_free_mem()
l = []
for i in tqdm(range(100000000)):
l.append((1,2,3,4,5,)
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码二:list 版

1
2
3
4
5
6
7
8
9
10
11
12
13
from tqdm import tqdm
def get_free_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])
f1 = get_free_mem()
l = []
for i in tqdm(range(100000000)):
l.append([1,2,3,4,5,])
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码三:list 版(使用同一个对象)

1
2
3
4
5
6
7
8
f1 = get_free_mem()
l = []
x = [1,2,3,4,5,]
for i in tqdm(range(100000000)):
l.append(x)
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码四

1
2
3
4
5
6
7
f1 = get_free_mem()
l = []
for i in tqdm(range(50000000)):
l.append((i+1, i+2, i+3, i+4, i+5,))
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码五

1
2
3
4
5
6
7
f1 = get_free_mem()
l = []
for i in tqdm(range(50000000)):
l.append([i+1, i+2, i+3, i+4, i+5,])
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码六

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from tqdm import tqdm
import json

def get_frre_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])

f = open('/opt/sdb1/bitcoin_raw/index/block_foward_450001_500000')
d = {}
freem = [get_frre_mem()]
for _ in tqdm(range(5000000)):
l = f.readline()
if not l:
print('finish')
break
l = l.split('\t')
d[int(l[0], 16)] = tuple([int(x, 16) for x in json.loads(l[2])])
cur_fm = get_frre_mem()
freem.append(cur_fm)
print((freem[0]-cur_fm)/1024/1024)

代码七

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from tqdm import tqdm
import json

def get_frre_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])

f = open('/opt/sdb1/bitcoin_raw/index/block_foward_450001_500000')
d = {}
freem = [get_frre_mem()]
for _ in tqdm(range(5000000)):
l = f.readline()
if not l:
print('finish')
break
l = l.split('\t')
d[int(l[0], 16)] = [int(x, 16) for x in json.loads(l[2])]
cur_fm = get_frre_mem()
freem.append(cur_fm)
print((freem[0]-cur_fm)/1024/1024)

代码八

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from tqdm import tqdm
import json

def get_frre_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])

f = open('/opt/sdb1/bitcoin_raw/index/block_foward_450001_500000')
d = {}
freem = [get_frre_mem()]
for _ in tqdm(range(5000000)):
l = f.readline()
if not l:
print('finish')
break
l = l.split('\t')
d[int(l[0], 16)] = (int(x, 16) for x in json.loads(l[2]))
cur_fm = get_frre_mem()
freem.append(cur_fm)
print((freem[0]-cur_fm)/1024/1024)

评论无需登录,可以匿名,欢迎评论!