Trích dẫn tài liệu Python
Phiên bản kinh điển của câu trả lời này hiện đang ở câu hỏi Dupliquee: sự khác biệt giữa các mô -đun phân luồng và đa xử lý là gì?
Tôi đã nhấn mạnh các trích dẫn tài liệu Python chính về quy trình so với các luồng và Gil AT: Khóa phiên dịch toàn cầu [GIL] toàn cầu trong CPython là gì?
Xử lý so với thí nghiệm chủ đề
Tôi đã làm một chút điểm chuẩn để thể hiện sự khác biệt một cách cụ thể hơn.
Trong điểm chuẩn, tôi đã định thời gian CPU và IO bị ràng buộc cho các số lượng chủ đề khác nhau trên CPU 8 hyperthread. Công việc được cung cấp cho mỗi chủ đề luôn giống nhau, như vậy nhiều chủ đề có nghĩa là nhiều công việc được cung cấp hơn.
Kết quả là:
Sơ đồ dữ liệu.
Conclusions:
Đối với công việc ràng buộc CPU, đa xử lý luôn nhanh hơn, có lẽ là do Gil
cho công việc ràng buộc IO. Cả hai đều chính xác là cùng một tốc độ
Chủ đề chỉ tỷ lệ lên tới khoảng 4 lần thay vì 8 lần dự kiến vì tôi đang sử dụng 8 máy Hyperthread.
Tương phản rằng với một công việc gắn CPU CPU POSIX đạt tốc độ 8 lần dự kiến: 'thật', 'người dùng' và 'sys' có ý nghĩa gì trong đầu ra của thời gian [1]?
TODO: Tôi không biết lý do cho điều này, phải có sự thiếu hiệu quả khác của Python.
Mã kiểm tra:
#!/usr/bin/env python3
import multiprocessing
import threading
import time
import sys
def cpu_func[result, niters]:
'''
A useless CPU bound function.
'''
for i in range[niters]:
result = [result * result * i + 2 * result * i * i + 3] % 10000000
return result
class CpuThread[threading.Thread]:
def __init__[self, niters]:
super[].__init__[]
self.niters = niters
self.result = 1
def run[self]:
self.result = cpu_func[self.result, self.niters]
class CpuProcess[multiprocessing.Process]:
def __init__[self, niters]:
super[].__init__[]
self.niters = niters
self.result = 1
def run[self]:
self.result = cpu_func[self.result, self.niters]
class IoThread[threading.Thread]:
def __init__[self, sleep]:
super[].__init__[]
self.sleep = sleep
self.result = self.sleep
def run[self]:
time.sleep[self.sleep]
class IoProcess[multiprocessing.Process]:
def __init__[self, sleep]:
super[].__init__[]
self.sleep = sleep
self.result = self.sleep
def run[self]:
time.sleep[self.sleep]
if __name__ == '__main__':
cpu_n_iters = int[sys.argv[1]]
sleep = 1
cpu_count = multiprocessing.cpu_count[]
input_params = [
[CpuThread, cpu_n_iters],
[CpuProcess, cpu_n_iters],
[IoThread, sleep],
[IoProcess, sleep],
]
header = ['nthreads']
for thread_class, _ in input_params:
header.append[thread_class.__name__]
print[' '.join[header]]
for nthreads in range[1, 2 * cpu_count]:
results = [nthreads]
for thread_class, work_size in input_params:
start_time = time.time[]
threads = []
for i in range[nthreads]:
thread = thread_class[work_size]
threads.append[thread]
thread.start[]
for i, thread in enumerate[threads]:
thread.join[]
results.append[time.time[] - start_time]
print[' '.join['{:.6e}'.format[result] for result in results]]
GitHub ngược dòng + Mã vẽ âm mưu trên cùng thư mục.
Đã thử nghiệm trên Ubuntu 18.10, Python 3.6.7, trong máy tính xách tay Lenovo ThinkPad p51 với CPU: Intel Core i7-7820HQ CPU [4 lõi / 8 000L7 [3.000 Mb/s].
Trực quan hóa các luồng nào đang chạy tại một thời điểm nhất định
Bài đăng này //rohanvarma.me/gil/ đã dạy tôi rằng bạn có thể chạy một cuộc gọi lại bất cứ khi nào một luồng được lên lịch với đối số target=
của threading.Thread
và tương tự cho multiprocessing.Process
.
Điều này cho phép chúng tôi xem chính xác chủ đề nào chạy mỗi lần. Khi điều này được thực hiện, chúng ta sẽ thấy một cái gì đó giống như [tôi đã tạo ra biểu đồ cụ thể này]:
+--------------------------------------+
+ Active threads / processes +
+-----------+--------------------------------------+
|Thread 1 |******** ************ |
| 2 | ***** *************|
+-----------+--------------------------------------+
|Process 1 |*** ************** ****** **** |
| 2 |** **** ****** ** ********* **********|
+-----------+--------------------------------------+
+ Time --> +
+--------------------------------------+
Điều đó sẽ cho thấy rằng:
- Chủ đề được nối tiếp hoàn toàn bởi Gil
- Các quy trình có thể chạy song song