【问题标题】:why is my memory footprint blowing up in this greedy approach to tsp?为什么我的内存占用在这种贪婪的 tsp 方法中爆炸?
【发布时间】:2021-08-21 16:14:01
【问题描述】:

我有一个任务是使用贪婪的方法来满足 TSP。问题有33708个城市。因为我在之前的作业中有很多有用的工具,所以我决定重用这种方法并预先计算距离。

所以这仅超过十亿个条目(33708 选择 2),每个条目都非常适合 float32。 x 和 y 坐标同样是数字 $|n|

我的蟒蛇是:

def get_distance(left, right):
  """ return the euclidean distance between tuples left and right, which are coordinates"""
  return ((left[0] - right[0]) ** 2 + (left[1] - right[1]) ** 2) ** 0.5

# precompute all distances
distances = {}
for i in range(len(cities)):
  for j in range(i + 1, len(cities)):
    d = get_distance(cities[i], cities[j])
    distances[frozenset((i, j)))] = d

我预计这会占用 (3 * 32b) * 568m ≈ 6.7 GB 的内存。但事实上,在我的 jupyter notebook 中观看实时运行时,它似乎甚至超过了 35GB。 (442s 和计数)我不得不杀死它,因为我很好地进入了我的交换空间并且它减慢了很多。有谁知道为什么这么大?

更新:再次尝试使用 tuple(sorted((i,j))) -- 但在 110 秒时它已经是 15GB 并且还在计数

尺寸

>>> import sys
>>> a = frozenset((1,2))
>>> sys.getsizeof(a)
216
>>> sys.getsizeof(tuple(sorted((1,2))))
56
>>> sys.getsizeof(1)
28

python 中是否有类似 float32 和 int16 的东西? -- ans: numpy 有它们

更新尝试:

from numpy import float32, int16
from itertools import combinations
import sys

def get_distance(left, right):
  """ return the euclidean distance between tuples left and right, which are coordinates"""
  return float32(((left[0] - right[0]) ** 2 + (left[1] - right[1]) ** 2) ** 0.5)

# precompute all distances
distances = {}
for i, j in combinations(range(len(cities)), 2):
    distances[tuple(sorted((int16(i), int16(j))))] = get_distance(cities[i], cities[j])

print(sys.getsizeof(distances))

观察到的尺寸:

  • cities = cities[:2]:232
  • cities = cities[:3]:也是232
  • cities = cities[:10]:2272
  • cities = cities[:100]:147552
  • cities = cities[:1000]:20971608 (20MB)
  • cities = cities[:10000] : 2684354656 (2.6GB)

请注意,即使我们接近 5000 万个条目,即 10000 个选择 2(占数据总大小的 10%),增长率也不会随数据成比例:

  • 2684354656/(1000 选择 2 / 100 选择 2 * 20971608) ≈ 1.27
  • 20971608/(1000 选 2 / 100 选 2 * 147552) ≈ 1.4

我决定停止对完整城市列表的尝试,因为我的操作系统快照内存增长到超过 30GB 并且我要进行交换。这意味着,即使最终对象最终有那么大,笔记本所需的内存量仍然要大得多。

【问题讨论】:

  • 什么是 3 * 32b?
  • 1x32b 是距离。还有 2 个是 x 和 y 坐标。 (更新了问题描述)。在分配时,它们是dij
  • 试试sys.getsizeof(frozenset((1, 2)))
  • 实际键/值对象足迹相比,是的,字典足迹可能微不足道。但它比你最初的想法要大。例如,sys.getsizeof(dict.fromkeys(range(10**6))) / 1e6 给了我41.943136,所以每个项目大约 42 字节的字典占用空间。
  • 所有ij对象的内存可以通过重用相同的对象来减少很多,例如没有很多对象都具有相同的值3141。就像for i, j in itertools.combinations(range(len(cities)), 2): .但是无论如何,集合/元组/字典仍然会花费很多。

标签: python jupyter-notebook np-complete


【解决方案1】:

由于动态类型和引用计数,Python 对象会产生开销。绝对最小对象object() 的大小为16 字节(在64 位机器上)。 8 字节引用计数,8 字节类型指针。 python 对象可以比那个小。 floatint 稍大,至少 24 字节。 list 至少是一个指针数组,它增加了一个额外的 8 字节。因此,十亿个整数列表的最小可能内存占用是32 * 500_000_000 ~= 16Gb。 setsdicts 甚至比这更大,因为它们每个元素存储的不仅仅是一个指针。

使用numpy(也许stdlib array 模块已经够用了)。

(注意:numpy float32 类型也不能小于 16 字节)

【讨论】:

  • “因此,十亿个整数列表的内存占用很小” - 仅当它们都不同时。如果你重复使用它们,就像在这里很容易做到的那样,它会更少。
  • @KellyBundy 那么它至少仍然是 8 * 500_000_000 ~= 4GB,仍然超过 OP 的预期。 (我也忽略了3 *
  • @KellyBundy 请记住,我只是在谈论列表。与元组字典无关。
  • 我只想指出,这在很大程度上是凯利的回答,但他确实联系了我或任何人来提供它,因为他觉得这不是一个答案。但是我愿意。我感谢你们俩。 (不,实际上它似乎不会让我低于 30gb,但这不是要求,我的理解是。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-30
  • 2015-01-12
  • 2019-12-23
  • 1970-01-01
  • 2021-10-22
  • 1970-01-01
  • 2018-02-17
相关资源
最近更新 更多