首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >读入一个6G的文本文件,为什么需要60G内存,如何减少?

读入一个6G的文本文件,为什么需要60G内存,如何减少?
EN

Stack Overflow用户
提问于 2019-08-26 01:49:07
回答 1查看 71关注 0票数 2

我有一个6G的tsv文件。文件的内容是数字,最大的数字是57134205。该文件实际上是一个查找表,每行的第一个数字是键,该行的后续数字是映射到该键的值。在python dict的形式中,它类似于:

lookup1=1,2,3,4

lookup2=7,8

..。

因此,我认为这是一些琐碎的东西,并尝试逐行读取文件(我使用的是python 3.7):

代码语言:javascript
复制
R={}
with open(filename) as f:
    for line in f:
        l=list(map(int,line.split('\t')))
        R[l[0]] = set(l[1:])

但这毁了我的机器,它有64G的内存(在读完文件之前出现了内存错误)。我怀疑这是由于数字的int类型造成的,所以我尝试将它们保留为str,这相当于更改了行

代码语言:javascript
复制
l=list(map(int,l.split('\t')))

代码语言:javascript
复制
l=line.split('\t')

这仍然失败,我尝试显式地使用np.uint32

代码语言:javascript
复制
l=[np.uint32(i) for i in line.split('\t')]

还是失败了。

现在我已经没有主意了。我知道数据结构会比数字本身占用更多的空间,但我仍然很惊讶这个6G文件不能放在64G内存中,除非代码中有一些我完全忽略的bug。如果有什么建议,请告诉我。谢谢你的帮助!

EN

回答 1

Stack Overflow用户

发布于 2019-08-26 18:42:25

每当您发现自己在存储同构数据数组的同时试图节省内存时,您都可以从the array module中受益。

在您的例子中,您应该能够将数据放在一个连续的内存区域中,并像单链表一样对其进行组织,以便以后能够对其进行搜索。您将节省大量内存,但您必须牺牲字典提供的O(1)搜索复杂性,而使用线性搜索算法提供的O(number of loop iterations, apparently?) one (请参阅下面的get_value_at_index函数),如果使用双向链表并考虑前一次搜索中值的偏移,则线性搜索算法可能会进一步优化。

代码语言:javascript
复制
import random
import array
import sys

def write_file(fname: str, nlines: int) -> int:
    '''
    Write `nlines` lines to file `fname`.
    Return the number of entries written.
    '''
    data_range = range(57134205)
    nentries_total = 0
    with open(fname, 'w') as f:
        for x in range(nlines):
            nentries = random.randint(0, 30)
            data = random.sample(data_range, nentries)
            data.insert(0, x)
            nentries_total += nentries + 1

            data_str = ','.join(map(str, data))
            f.write(data_str + '\n')

    return nentries_total

def store_that(fname: str) -> array.array:
    result = array.array('L')
    next_index = 0
    with open(fname) as f:
        for line in f:
            index, *values = map(int, line.split(','))

            next_index += len(values) + 2
            result.append(index)
            result.append(next_index)
            result.extend(values)

    return result

def get_value_at_index(array: array.array, index: int) -> array.array:
    current_index_offset = 0
    next_index_offset = array[current_index_offset + 1]
    current_index = array[current_index_offset]

    while current_index != index:
        current_index = array[next_index_offset]
        current_index_offset = next_index_offset
        next_index_offset = array[next_index_offset + 1]

    return array[current_index_offset + 2:next_index_offset]


FNAME = 'test.txt'
number_of_keys = 2_000
total_size = write_file(FNAME, number_of_keys)
data = store_that(FNAME)

for x in range(10):
    index = random.randint(0, number_of_keys)
    value = get_value_at_index(data, index)
    print(f'data[{index}] == {value}')
print()

actual_size = data.buffer_info()[1]
size_of_object, size_of_data = sys.getsizeof(data), actual_size * data.itemsize
print(f'Key count     : {number_of_keys: >7} keys')
print(f'Element count : {total_size: >7} elements')
print(f'With indices  : {actual_size: >7} elements ({actual_size / total_size - 1:.3%} overhead)')
print(f'Size of data  : {size_of_data:_} bytes')
print(f'Size of object: {size_of_object:_} bytes    ({size_of_object / size_of_data - 1:.3%} overhead)')
print(f'\nTotal overhead: {size_of_object / (total_size * data.itemsize) - 1:.3%}')

通过这个设置,我得到了如下输出:

代码语言:javascript
复制
Key count     :    2000 keys
Element count :   31477 elements
With indices  :   33477 elements (6.354% overhead)
Size of data  : 133_908 bytes
Size of object: 136_808 bytes    (2.166% overhead)

Total overhead: 8.657%

请注意,您的结果会有所不同,因为此代码随机化了要存储的条目的数量。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57648377

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档