2016-04-30 28 views
0

我在這裏有一個python程序,用於將DIMACS cnf格式文件轉換爲PLA格式。我正在讀取文件中的CNF子句並將它們存儲在列表中,然後對列表元素執行操作。 本程序適用於最多15,000行(子句)的較小文件,但當我嘗試在較大的文件上運行程序時,系統內存不足。我需要操作大約90,000到120,000行的文件。有人可以請建議一些更改以優化內存使用情況嗎?以下是我的程序:Python:減少大型文件讀取 - 存儲 - 操作 - 寫入程序中的RAM使用量

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) 
+0

從代碼中很難說清楚,但是您是否真的需要將所有行讀入內存?行之間是否存在依賴關係?或者你可以不使用迭代器來讀取行嗎?即使這意味着一次讀取文件的子句和另一次的其他東西?另外,如果實際數據不太可變,您可以查看基於\ __ slots \ __的類。或命名元組。口述是偉大的,但相當餓了。 –

+0

當我檢查代碼時,瓶頸函數是函數getPLAlist() 讓我來解釋一下這個問題和我的代碼: 假設每行都有一個子句: 例如。 2 -4 0在7個輸入的cnf中 我需要在第2個位置打印出0位,第4個位置打印1個,其餘5個位置在該行的PLA文件中打印爲「-0-1 ---」 所以我在temp_list 中用字符串「-------」創建一個列表然後用0和1代替第二個和第四個位置,然後將temp_list附加到PLA行的列表,即temp_list_total。 任何建議,我可以改善這種方法將不勝感激。 –

+0

是的,但沒有一個告訴我你需要一次返回整個列表。這就是@Robin Davis所說的。我曾經批量轉換數據庫導出,這會導致在500k +表格上爆炸(代碼按表格逐個運行,在崩潰之前會吸取3-4 gb)。沒有修復它,直到我意識到*行n *不依賴於之前或之後的行並開始使用** yield **。速度保持不變,ram回落到<100mb的水平。你在這個問題上拋出的任何東西都是浪費時間,恕我直言。 –

回答

2

一個詞:發電機。這裏的基本思想是每個函數只需要獲得下一行所需的工作量,而不再需要。這可以減少內存佔用。在這裏可以做更多的優化,但是我不太瞭解這個問題,或者沒有任何測試數據可以使用。這完全沒有經過測試,但它可能工作!

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) 
+0

當我檢查代碼時,我發現瓶頸是getPLAlist()讓我解釋一下問題和我的代碼:假設每行都有一個子句:例如, 2 -4 10 0在一個cnf中的15個輸入中,我需要在第二個位置輸出0,在第四個位置輸出1,其餘位置爲「-0-1 ----- 0 ---- - 「在該行的PLA文件中所以我在temp_list中創建了一個字符串」---------------「的列表然後用0代替第2,第4和第n個第10個位置, 1,0,然後我將temp_list附加到PLA行的列表,即temp_list_total。任何建議,我可以改善這一點,將不勝感激。 –

+0

我不認爲這是記憶瓶頸。問題是讀取整個文件,實際上是在內存中創建多個副本。要使用更少的內存,只需從文件中讀取一個子句,然後爲其生成pla,然後重複。 –

+0

謝謝你的建議。我會試試這個。 –