การเพิ่มผลลัพธ์พูลให้กับ dict

ฉันมีฟังก์ชันที่ยอมรับอินพุตสองตัวที่มาจากการรวมกันของ itertools และเอาต์พุตโซลูชัน อินพุตทั้งสองควรถูกจัดเก็บเป็นสิ่งทูเปิลที่สร้างคีย์ใน dict ในขณะที่ผลลัพธ์คือค่า

ฉันสามารถรวมสิ่งนี้และรับผลลัพธ์ทั้งหมดเป็นรายการ ซึ่งฉันสามารถแทรกลงในพจนานุกรมได้ทีละรายการ แต่ดูเหมือนว่าจะไม่มีประสิทธิภาพ มีวิธีรับผลลัพธ์เมื่อแต่ละงานเสร็จสิ้นและเพิ่มลงใน dict โดยตรงหรือไม่

โดยพื้นฐานแล้วฉันมีรหัสด้านล่าง:

all_solutions = {}
for start, goal in itertools.combinations(graph, 2):
    all_solutions[(start, goal)] = search(graph, start, goal)

ฉันพยายามขนานมันดังนี้:

all_solutions = {}
manager = multiprocessing.Manager()
graph_pool = manager.dict(graph)
pool = multiprocessing.Pool()
results = pool.starmap(search, zip(itertools.repeat(graph_pool),
                                   itertools.combinations(graph, 2)))
for i, start_goal in enumerate(itertools.combinations(graph, 2)):
    start, goal = start_goal[0], start_goal[1]
    all_solutions[(start, goal)] = results[i]

ซึ่งใช้งานได้จริง แต่วนซ้ำสองครั้งครั้งแรกในพูลและอีกครั้งเพื่อเขียนถึง dict (ไม่ต้องพูดถึงการแกะ tuple ที่เกะกะ)


person Teknophilia    schedule 14.02.2018    source แหล่งที่มา
comment
FYI ไม่จำเป็นต้องแกะสิ่งทูเพิลที่ยุ่งยาก คุณเพียงแค่ต้องการวงเล็บเพิ่มเติม: for i, (start, goal) in enumerate(itertools.combinations(graph, 2)): แตกไฟล์ลงใน start และ goal โดยตรง (วงเล็บ ไม่ใช่ เป็นทางเลือกในกรณีนี้ เพราะคุณต้องการให้มันชัดเจนว่าเป็นการแกะกล่องแบบซ้อนที่เกิดขึ้น)   -  person ShadowRanger    schedule 14.02.2018
comment
@ShadowRanger อ่า เป็นเรื่องดีที่ได้รู้ ขอบคุณ!   -  person Teknophilia    schedule 14.02.2018


คำตอบ (1)


สิ่งนี้เป็นไปได้ คุณเพียงแค่ต้องเปลี่ยนไปใช้ฟังก์ชัน Lazy Mapping (ไม่ใช่ map หรือ starmap ซึ่งจะต้องคำนวณผลลัพธ์ทั้งหมดให้เสร็จก่อนจึงจะเริ่มใช้งานได้):

from functools import partial
from itertools import tee

manager = multiprocessing.Manager()
graph_pool = manager.dict(graph)
pool = multiprocessing.Pool()

# Since you're processing in order and in parallel, tee might help a little
# by only generating the dict keys/search arguments once. That said, 
# combinations of n choose 2 are fairly cheap; the overhead of tee's caching
# might overwhelm the cost of just generating the combinations twice
startgoals1, startgoals2 = tee(itertools.combinations(graph, 2))

# Use partial binding of search with graph_pool to be able to use imap
# without a wrapper function; using imap lets us consume results as they become
# available, so the tee-ed generators don't store too many temporaries
results = pool.imap(partial(search, graph_pool), startgoals2))

# Efficiently create the dict from the start/goal pairs and the results of the search
# This line is eager, so it won't complete until all the results are generated, but
# it will be consuming the results as they become available in parallel with
# calculating the results
all_solutions = dict(zip(startgoals1, results))
person ShadowRanger    schedule 14.02.2018
comment
ว้าว นั่นแหละครับ บางส่วนคือสิ่งที่ฉันกำลังมองหา ขอบคุณ! - person Teknophilia; 14.02.2018
comment
@Teknophilia: ใช่แล้ว คำเตือน: partial จะช้าถ้าอาร์กิวเมนต์ที่ถูกผูกไว้มีราคาแพงถึง pickle ในกรณีนี้ เป็น dict ที่มีการจัดการซึ่งมีไว้สำหรับ multiprocessing ดังนั้นต้นทุนการดองเป็นเพียงข้อมูลที่จำเป็นในการเชื่อมต่อใหม่กับผู้จัดการ (ค่อนข้างถูก) ไม่ใช่การคัดลอกข้อมูลทั้งหมด แต่หากอาร์กิวเมนต์มีขนาดใหญ่/แพงในการดอง partial ไม่ใช่หนทางไป คำตอบของฉันสำหรับคำถามนั้น อธิบายว่าทำไมฟังก์ชัน wrapper ส่วนกลางที่มีอาร์กิวเมนต์เริ่มต้นจึงเป็นแนวคิดที่ดีกว่าในกรณีเช่นนี้ - person ShadowRanger; 14.02.2018
comment
ขอบคุณสำหรับหัวขึ้น. dict ที่ได้รับการจัดการนั้นค่อนข้างใหญ่ แต่ถ้าฉันเข้าใจถูกต้อง มันเพิ่งจะเชื่อมต่อใหม่ - ไม่ซ้ำกัน - ดังนั้นจึงไม่น่าจะเป็นปัญหา ฉันกำลังอ่านเรื่องนี้และคำตอบของคุณช่วยได้มากในการทำความเข้าใจ - person Teknophilia; 14.02.2018