यह एक चर्चा है, देखें: https://discuss.python.org/t/speed-up-shutil-copytree/62078। यदि आपके पास कोई विचार है, तो कृपया मुझे भेजें!
श्यूटिल पायथन में एक बहुत ही उपयोगी मौडल है। आप इसे जीथब में पा सकते हैं: https://github.com/python/cpython/blob/master/Lib/shutil.py
shutil.copytree एक फ़ंक्शन है जो एक फ़ोल्डर को दूसरे फ़ोल्डर में कॉपी करता है।
इस फ़ंक्शन में, यह कॉपी करने के लिए _copytree फ़ंक्शन को कॉल करता है।
_copytree क्या करता है?
जब फ़ाइलों की संख्या बड़ी हो या फ़ाइल का आकार बड़ा हो तो कॉपीट्री की गति बहुत तेज़ नहीं होती है।
यहाँ परीक्षण करें:
import os import shutil os.mkdir('test') os.mkdir('test/source') def bench_mark(func, *args): import time start = time.time() func(*args) end = time.time() print(f'{func.__name__} takes {end - start} seconds') return end - start # write in 3000 files def write_in_5000_files(): for i in range(5000): with open(f'test/source/{i}.txt', 'w') as f: f.write('Hello World' os.urandom(24).hex()) f.close() bench_mark(write_in_5000_files) def copy(): shutil.copytree('test/source', 'test/destination') bench_mark(copy)
परिणाम यह है:
write_in_5000_files में 4.084963083267212 सेकंड लगते हैं
कॉपी करने में 27.12768316268921 सेकंड लगते हैं
कॉपी करने की प्रक्रिया को तेज़ करने के लिए मैं मल्टीथ्रेड का उपयोग करता हूं। और मैं फ़ंक्शन का नाम बदल देता हूं _copytree_single_threaded एक नया फ़ंक्शन जोड़ता हूं _copytree_multithreaded। यहाँ Copytree_multithreaded है:
def _copytree_multithreaded(src, dst, symlinks=False, ignore=None, copy_function=shutil.copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False, max_workers=4): """Recursively copy a directory tree using multiple threads.""" sys.audit("shutil.copytree", src, dst) # get the entries to copy entries = list(os.scandir(src)) # make the pool with ThreadPoolExecutor(max_workers=max_workers) as executor: # submit the tasks futures = [ executor.submit(_copytree_single_threaded, entries=[entry], src=src, dst=dst, symlinks=symlinks, ignore=ignore, copy_function=copy_function, ignore_dangling_symlinks=ignore_dangling_symlinks, dirs_exist_ok=dirs_exist_ok) for entry in entries ] # wait for the tasks for future in as_completed(futures): try: future.result() except Exception as e: print(f"Failed to copy: {e}") raise
मैं मल्टीथ्रेड का उपयोग करने या न करने का निर्णय जोड़ता हूं।
if len(entries) >= 100 or sum(os.path.getsize(entry.path) for entry in entries) >= 100*1024*1024: # multithreaded version return _copytree_multithreaded(src, dst, symlinks=symlinks, ignore=ignore, copy_function=copy_function, ignore_dangling_symlinks=ignore_dangling_symlinks, dirs_exist_ok=dirs_exist_ok) else: # single threaded version return _copytree_single_threaded(entries=entries, src=src, dst=dst, symlinks=symlinks, ignore=ignore, copy_function=copy_function, ignore_dangling_symlinks=ignore_dangling_symlinks, dirs_exist_ok=dirs_exist_ok)
मैं स्रोत फ़ोल्डर में 50000 फ़ाइलें लिखता हूं। बेंचमार्क:
def bench_mark(func, *args): import time start = time.perf_counter() func(*args) end = time.perf_counter() print(f"{func.__name__} costs {end - start}s")
इसमें लिखें:
import os os.mkdir("Test") os.mkdir("Test/source") # write in 50000 files def write_in_file(): for i in range(50000): with open(f"Test/source/{i}.txt", 'w') as f: f.write(f"{i}") f.close()
दो तुलना:
def copy1(): import shutil shutil.copytree('test/source', 'test/destination1') def copy2(): import my_shutil my_shutil.copytree('test/source', 'test/destination2')
copy1 की लागत 173.04780609999943s
कॉपी2 की लागत 155.81321870000102s
copy2,copy1 से काफी तेज है। आप कई बार चला सकते हैं।
मल्टीथ्रेड का उपयोग प्रतिलिपि बनाने की प्रक्रिया को तेज़ कर सकता है। लेकिन इससे मेमोरी का उपयोग बढ़ जाएगा. लेकिन हमें कोड में मल्टीथ्रेड को दोबारा लिखने की आवश्यकता नहीं है।
"बैरी स्कॉट" को धन्यवाद। मैं उनके सुझाव का पालन करूंगा :
आपको async I/O का उपयोग करके कम ओवरहेड के लिए समान सुधार मिल सकता है।
मैं ये कोड लिखता हूं:
import os import shutil import asyncio from concurrent.futures import ThreadPoolExecutor import time # create directory def create_target_directory(dst): os.makedirs(dst, exist_ok=True) # copy 1 file async def copy_file_async(src, dst): loop = asyncio.get_event_loop() await loop.run_in_executor(None, shutil.copy2, src, dst) # copy directory async def copy_directory_async(src, dst, symlinks=False, ignore=None, dirs_exist_ok=False): entries = os.scandir(src) create_target_directory(dst) tasks = [] for entry in entries: src_path = entry.path dst_path = os.path.join(dst, entry.name) if entry.is_dir(follow_symlinks=not symlinks): tasks.append(copy_directory_async(src_path, dst_path, symlinks, ignore, dirs_exist_ok)) else: tasks.append(copy_file_async(src_path, dst_path)) await asyncio.gather(*tasks) # choose copy method def choose_copy_method(entries, src, dst, **kwargs): if len(entries) >= 100 or sum(os.path.getsize(entry.path) for entry in entries) >= 100 * 1024 * 1024: # async version asyncio.run(copy_directory_async(src, dst, **kwargs)) else: # single thread version shutil.copytree(src, dst, **kwargs) # test function def bench_mark(func, *args): start = time.perf_counter() func(*args) end = time.perf_counter() print(f"{func.__name__} costs {end - start:.2f}s") # write in 50000 files def write_in_50000_files(): for i in range(50000): with open(f"Test/source/{i}.txt", 'w') as f: f.write(f"{i}") def main(): os.makedirs('Test/source', exist_ok=True) write_in_50000_files() # 单线程复制 def copy1(): shutil.copytree('Test/source', 'Test/destination1') def copy2(): shutil.copytree('Test/source', 'Test/destination2') # async def copy3(): entries = list(os.scandir('Test/source')) choose_copy_method(entries, 'Test/source', 'Test/destination3') bench_mark(copy1) bench_mark(copy2) bench_mark(copy3) shutil.rmtree('Test') if __name__ == "__main__": main()
आउटपुट:
copy1 की लागत 187.21s
कॉपी2 की लागत 244.33 सेकेंड है
कॉपी3 की लागत 111.27 सेकेंड है
आप देख सकते हैं कि एसिंक संस्करण सिंगल थ्रेड संस्करण से तेज़ है। लेकिन सिंगल थ्रेड संस्करण मल्टी-थ्रेड संस्करण की तुलना में तेज़ है। (शायद मेरा परीक्षण वातावरण बहुत अच्छा नहीं है, आप कोशिश कर सकते हैं और अपना परिणाम मुझे उत्तर के रूप में भेज सकते हैं)
धन्यवाद बैरी स्कॉट !
Async एक अच्छा विकल्प है। लेकिन कोई भी समाधान पूर्ण नहीं होता. यदि आपको कोई समस्या आती है तो आप मुझे उत्तर के रूप में भेज सकते हैं।
python.org पर चर्चा लिखने का यह मेरा पहला अवसर है। यदि कोई समस्या हो तो कृपया मुझे बताएं। धन्यवाद।
मेरा जीथब: https://github.com/mengqinyuan
मेरा Dev.to: https://dev.to/mengqinyuan
अस्वीकरण: उपलब्ध कराए गए सभी संसाधन आंशिक रूप से इंटरनेट से हैं। यदि आपके कॉपीराइट या अन्य अधिकारों और हितों का कोई उल्लंघन होता है, तो कृपया विस्तृत कारण बताएं और कॉपीराइट या अधिकारों और हितों का प्रमाण प्रदान करें और फिर इसे ईमेल पर भेजें: [email protected] हम इसे आपके लिए यथाशीघ्र संभालेंगे।
Copyright© 2022 湘ICP备2022001581号-3