【问题标题】:Python: Reducing the RAM usage in a large file read-store-operate-write programPython:减少大文件读取-存储-操作-写入程序中的 RAM 使用量
【发布时间】:2016-04-30 04:00:17
【问题描述】:

我这里有一个 python 程序,可以将 DIMACS cnf 格式文件转换为 PLA 格式。我正在从文件中读取 CNF 子句并将它们存储在列表中,然后对列表元素运行操作。 该程序适用于最多 15,000 行(子句)的较小文件,但是当我尝试在较大文件上运行程序时系统内存不足。我需要对大约 90,000 到 120,000 行的文件进行操作。有人可以建议一些更改以优化内存使用吗?下面是我的程序:

import sys

#Input: CNF file path
#Output: dictionary of params and clauses from cnf file
def readCNFFile(name):
    lines_str = []
    inputs_dict = {}
    clause_list = []
    with open(name, "r") as f:
        for line in f.readlines():
            lines_str.append(str(line))
        for current in range(len(lines_str)):
            li=lines_str[current].strip()
            if li.startswith("c"):
                pass
            elif li.startswith("C"):
                pass
            elif li.startswith("p"):
                inputs_dict['params'] = li
            else:
                clause_list.append(li)
        inputs_dict['clauses']=clause_list
        f.close()
    return inputs_dict

#Input: inputs_dict["clauses"]
#Output: list of clauses split into integers
def getClauses(clause_list):
    for current in range(len(clause_list)):
        temp = clause_list[current].split()
        clause_list[current] = temp
    for current in range(len(clause_list)):
        nums = [int(n) for n in clause_list[current]]
        clause_list[current]= nums
    return clause_list

#Input: inputs_dict["params"]
#Output: number of inputs in PLA
def getNumInputs(param_list):
    param = param_list.split()
    num_inputs = int(param[2])
    return num_inputs

#Input: inputs_dict["params"]
#Output: number of products in PLA
def getNumProducts(param_list):
    param = param_list.split()
    num_prod = int(param[3])
    return num_prod

#Inputs: 1. list of clauses split into integers
#        2. number of inputs in PLA
#Output: list of products for PLA file    
def getPLAlist(clause_list, num_inputs):
    s = "-"
    temp_list_total=[]
    for current in range(len(clause_list)):
        temp_list = []
        for index in range(1,(num_inputs+1)):
            temp_list.append("-")
        for index in range(len(clause_list[current])):
            for i in range(1,(num_inputs+1)):
                if(abs(clause_list[current][index])==i):
                    if(clause_list[current][index]<0):
                        temp_list[i-1]="1"
                    else:
                        temp_list[i-1]="0"
        temp_list_total.append(temp_list)
    return temp_list_total 

#Inputs: 1. input CNF file path
#        2. list of products for PLA file
#        3. number of inputs in PLA
#        4. number of products in PLA
#Output: print PLA file
def printPLAfile(inputFile, PLA_list, num_inputs, num_prod):
    outputfile = inputFile.split(".")[0] + "_pla2.pla"
    with open(outputfile, "w" ) as file_out:
        file_out.write(".i ")
        file_out.write(str(num_inputs))
        file_out.write("\n.o 1")
        file_out.write("\n.p ")
        file_out.write(str(num_prod))
        file_out.write("\n")
        for current in range(len(PLA_list)):
            for index in range(len(PLA_list[current])):
                file_out.write(PLA_list[current][index],)
            file_out.write(" 1 \n")
        file_out.write(".e")

#Get .pla file from .cnf 
#input: CNF File path
def convert_CNF_2_PLA(name):
    inputs = readCNFFile(name)
    clause_list = getClauses(inputs['clauses'])
    num_vars = getNumInputs(inputs['params'])
    num_clause = getNumProducts(inputs['params'])
    PLA_list = getPLAlist(clause_list, num_vars)
    printPLAfile(name, PLA_list, num_vars, num_clause)

if __name__ == '__main__':
    name = sys.argv[1]
    convert_CNF_2_PLA(name)

【问题讨论】:

  • 从您的代码中很难分辨,但是您真的需要将所有行读入内存吗?行之间是否存在依赖关系?或者你不能使用迭代器来读取行吗?即使这意味着为子句读取文件一次,为其他内容再次读取文件?此外,如果实际数据不太可变,您可以查看基于 _slots_ 的类。或命名元组。字典很棒,但对内存很饥渴。
  • 当我检查代码时,瓶颈函数是函数 getPLAlist() 让我解释一下问题和我的代码:假设我在每一行都有一个子句:例如。 7 个输入的 cnf 中的 2 -4 0 我需要在 PLA 文件中为该行打印第二个位置的 0 和第四个位置的 1 个,其余 5 个位置为“-0-1---” 所以我我在 temp_list 中创建一个带有字符串“-------”的列表,然后用 0 和 1 替换第 2 个和第 4 个位置,然后我将 temp_list 附加到 PLA 行的列表中,即 temp_list_total。任何我可以改进此方法的建议将不胜感激。
  • 是的,但这些都没有告诉我您需要立即返回整个列表。这也是@Robin Davis 所说的。我曾经有一批转换数据库导出,这会在 500k+ 表上爆炸(代码逐表运行,在崩溃前会吸收 3-4 GB)。直到我意识到 row n 不依赖于之前或之后的行并开始使用 yield 之前,什么都没有解决它。速度保持不变,ram 又回到了

标签: python python-2.7 python-3.x conjunctive-normal-form


【解决方案1】:

一个词:发电机。这里的基本思想是每个函数只做获得下一行所需的工作量,而不是更多。这使内存占用很小。肯定有更多的优化可以在这里完成,但我对问题的理解不够好,或者有任何测试数据可以使用。这是完全未经测试的,但它可能会起作用!

import sys

def readCNFParams(name):
    with open(name, "r") as f:
        for line in f:
            li=line.strip()
            if li[0] == "p":
                return li

def readCNFClauses(name):
    with open(name, "r") as f:
        for line in f:
            li=line.strip()
            if li[0] in ("c", "C", "p")
                continue
            else:
                yield li

#Input: inputs_dict["clauses"]
#Output: list of clauses split into integers
def getClauses(clause_list):
    clause_list = (c.split() for c in clause_list)
    return ([int(n) for n in c] for c in clause_list)

#Input: inputs_dict["params"]
#Output: number of inputs in PLA
def getNumInputs(param_list):
    param = param_list.split()
    num_inputs = int(param[2])
    return num_inputs

#Input: inputs_dict["params"]
#Output: number of products in PLA
def getNumProducts(param_list):
    param = param_list.split()
    num_prod = int(param[3])
    return num_prod

#Inputs: 1. list of clauses split into integers
#        2. number of inputs in PLA
#Output: list of products for PLA file    
def getPLAlist(clause_list, num_inputs):
    s = "-"
    for current in clause_list:
        temp_list = []
        for index in range(1,(num_inputs+1)):
            temp_list.append("-")
        for index in range(len(current)):
            for i in range(1,(num_inputs+1)):
                if(abs(current[index])==i):
                    if(current[index]<0):
                        temp_list[i-1]="1"
                    else:
                        temp_list[i-1]="0"
        yield temp_list

#Inputs: 1. input CNF file path
#        2. list of products for PLA file
#        3. number of inputs in PLA
#        4. number of products in PLA
#Output: print PLA file
def printPLAfile(inputFile, PLA_list, num_inputs, num_prod):
    outputfile = inputFile.split(".")[0] + "_pla2.pla"
    with open(outputfile, "w" ) as file_out:
        file_out.write(".i ")
        file_out.write(str(num_inputs))
        file_out.write("\n.o 1")
        file_out.write("\n.p ")
        file_out.write(str(num_prod))
        file_out.write("\n")
        for current in PLA_list:
            for index in range(len(current)):
                file_out.write(current[index],)
            file_out.write(" 1 \n")
        file_out.write(".e")

#Get .pla file from .cnf 
#input: CNF File path
def convert_CNF_2_PLA(name):
    clauses = readCNFClauses(name)
    clause_list = getClauses(clauses)
    params = readCNFParams(name)
    num_vars = getNumInputs(params)
    num_clause = getNumProducts(inputs['params'])
    PLA_list = getPLAlist(clause_list, num_vars)
    printPLAfile(name, PLA_list, num_vars, num_clause)

if __name__ == '__main__':
    name = sys.argv[1]
    convert_CNF_2_PLA(name)

【讨论】:

  • 当我检查代码时,我发现瓶颈是 getPLAlist() 让我解释一下问题和我的代码:假设我在每一行都有一个子句:例如。 2 -4 10 0 在 15 个输入的 cnf 中我需要在第 2 位打印 0,在第 4 位打印 1,其余位置的“-”为“-0-1-----0---- -” 在该行的 PLA 文件中所以我在 temp_list 中创建一个带有字符串“---------------” 的列表,然后用 0 替换第 2、第 4、第 10 个位置, 1, 0 然后我将 temp_list 附加到 PLA 行的列表中,即 temp_list_total。任何我可以改进的建议都将不胜感激。
  • 我不认为这是内存瓶颈。问题是读取整个文件,实际上在内存中制作了多个副本。要使用更少的内存,只需从文件中读取一个子句,并为其生成 pla,然后重复。
  • 感谢您的建议。我会试试这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-04
相关资源
最近更新 更多