- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
下面是实验代码,可以启动指定数量的工作进程,然后在每个进程内启动指定数量的工作线程,并执行获取 URL 的任务:
import multiprocessing
import sys
import time
import threading
import urllib.request
def main():
processes = int(sys.argv[1])
threads = int(sys.argv[2])
urls = int(sys.argv[3])
# Start process workers.
in_q = multiprocessing.Queue()
process_workers = []
for _ in range(processes):
w = multiprocessing.Process(target=process_worker, args=(threads, in_q))
w.start()
process_workers.append(w)
start_time = time.time()
# Feed work.
for n in range(urls):
in_q.put('http://www.example.com/?n={}'.format(n))
# Send sentinel for each thread worker to quit.
for _ in range(processes * threads):
in_q.put(None)
# Wait for workers to terminate.
for w in process_workers:
w.join()
# Print time consumed and fetch speed.
total_time = time.time() - start_time
fetch_speed = urls / total_time
print('{} x {} workers => {:.3} s, {:.1f} URLs/s'
.format(processes, threads, total_time, fetch_speed))
def process_worker(threads, in_q):
# Start thread workers.
thread_workers = []
for _ in range(threads):
w = threading.Thread(target=thread_worker, args=(in_q,))
w.start()
thread_workers.append(w)
# Wait for thread workers to terminate.
for w in thread_workers:
w.join()
def thread_worker(in_q):
# Each thread performs the actual work. In this case, we will assume
# that the work is to fetch a given URL.
while True:
url = in_q.get()
if url is None:
break
with urllib.request.urlopen(url) as u:
pass # Do nothing
# print('{} - {} {}'.format(url, u.getcode(), u.reason))
if __name__ == '__main__':
main()
下面是我运行这个程序的方式:
python3 foo.py <PROCESSES> <THREADS> <URLS>
例如,python3 foo.py 20 20 10000
创建 20 个工作进程,每个工作进程有 20 个线程(因此总共有 400 个工作线程)并获取 10000 个 URL。最后,该程序打印出获取 URL 所花费的时间以及平均每秒获取的 URL 数量。
请注意,在所有情况下,我实际上都在点击 www.example.com
域的 URL,即 www.example.com
不仅仅是一个占位符。换句话说,我不加修改地运行上面的代码。
我正在具有 8 GB RAM 和 4 个 CPU 的 Linode 虚拟专用服务器上测试此代码。它正在运行 Debian 9。
$ cat /etc/debian_version
9.9
$ python3
Python 3.5.3 (default, Sep 27 2018, 17:25:39)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
$ free -m
total used free shared buff/cache available
Mem: 7987 67 7834 10 85 7734
Swap: 511 0 511
$ nproc
4
下面是一些试运行,其中 400 个工作线程分布在 20 个工作进程之间(即,20 个工作进程中的每一个有 20 个工作线程)。在每次试验中,提取 10,000 个 URL。
结果如下:
$ python3 foo.py 20 20 10000
20 x 20 workers => 5.12 s, 1954.6 URLs/s
$ python3 foo.py 20 20 10000
20 x 20 workers => 5.28 s, 1895.5 URLs/s
$ python3 foo.py 20 20 10000
20 x 20 workers => 5.22 s, 1914.2 URLs/s
$ python3 foo.py 20 20 10000
20 x 20 workers => 5.38 s, 1859.8 URLs/s
$ python3 foo.py 20 20 10000
20 x 20 workers => 5.19 s, 1925.2 URLs/s
我们可以看到平均每秒约有 1900 个 URL 被抓取。当我使用 top
命令监视 CPU 使用率时,我看到每个 python3
工作进程消耗大约 10% 到 15% 的 CPU。
现在我以为我只有 4 个 CPU。即使我启动 20 个工作进程,在物理时间的任何时间点最多也只能运行 4 个进程。此外,由于全局解释器锁 (GIL),每个进程中只有一个线程(因此总共最多 4 个线程)可以在物理时间的任何时间点运行。
因此,我想如果我把进程数减少到4个,把每个进程的线程数增加到100个,这样总线程数还是400个,性能应该不会变差。
但测试结果表明,4 个进程每个包含 100 个线程的性能始终比 20 个进程每个包含 20 个线程的性能差。
$ python3 foo.py 4 100 10000
4 x 100 workers => 9.2 s, 1086.4 URLs/s
$ python3 foo.py 4 100 10000
4 x 100 workers => 10.9 s, 916.5 URLs/s
$ python3 foo.py 4 100 10000
4 x 100 workers => 7.8 s, 1282.2 URLs/s
$ python3 foo.py 4 100 10000
4 x 100 workers => 10.3 s, 972.3 URLs/s
$ python3 foo.py 4 100 10000
4 x 100 workers => 6.37 s, 1570.9 URLs/s
每个 python3
工作进程的 CPU 使用率在 40% 到 60% 之间。
只是为了比较,我记录的事实是情况 1 和情况 2 都优于我们在单个进程中拥有所有 400 个线程的情况。这肯定是由于全局解释器锁 (GIL)。
$ python3 foo.py 1 400 10000
1 x 400 workers => 13.5 s, 742.8 URLs/s
$ python3 foo.py 1 400 10000
1 x 400 workers => 14.3 s, 697.5 URLs/s
$ python3 foo.py 1 400 10000
1 x 400 workers => 13.1 s, 761.3 URLs/s
$ python3 foo.py 1 400 10000
1 x 400 workers => 15.6 s, 640.4 URLs/s
$ python3 foo.py 1 400 10000
1 x 400 workers => 13.1 s, 764.4 URLs/s
单个 python3
工作进程的 CPU 使用率在 120% 到 125% 之间。
同样,只是为了比较,这里是有 400 个进程时的结果,每个进程都有一个线程。
$ python3 foo.py 400 1 10000
400 x 1 workers => 14.0 s, 715.0 URLs/s
$ python3 foo.py 400 1 10000
400 x 1 workers => 6.1 s, 1638.9 URLs/s
$ python3 foo.py 400 1 10000
400 x 1 workers => 7.08 s, 1413.1 URLs/s
$ python3 foo.py 400 1 10000
400 x 1 workers => 7.23 s, 1382.9 URLs/s
$ python3 foo.py 400 1 10000
400 x 1 workers => 11.3 s, 882.9 URLs/s
每个 python3
工作进程的 CPU 使用率在 1% 到 3% 之间。
从每个案例中选择中位数结果,我们得到这个总结:
Case 1: 20 x 20 workers => 5.22 s, 1914.2 URLs/s ( 10% to 15% CPU/process)
Case 2: 4 x 100 workers => 9.20 s, 1086.4 URLs/s ( 40% to 60% CPU/process)
Case 3: 1 x 400 workers => 13.5 s, 742.8 URLs/s (120% to 125% CPU/process)
Case 4: 400 x 1 workers => 7.23 s, 1382.9 URLs/s ( 1% to 3% CPU/process
为什么 20 个进程 x 20 个线程的性能优于 4 个进程 x 100 个线程,即使我只有 4 个 CPU?
最佳答案
您的任务受 I/O 限制而不是 CPU 限制:线程大部分时间都处于 sleep 状态等待网络数据等,而不是使用 CPU。
因此,只要 I/O 仍然是瓶颈,添加比 CPU 更多的线程在这里是可行的。只有当线程数量如此之多以至于有足够多的线程同时准备好开始积极竞争 CPU 周期时(或者当您的网络带宽耗尽时,以先到者为准),这种影响才会消退。
至于为什么每个进程20个线程比每个进程100个线程快:这很可能是由于CPython的GIL。同一进程中的 Python 线程不仅需要等待 I/O,还需要等待彼此。
在处理 I/O 时,Python 机制:
如果同一进程中有足够多的线程,则在到达第 4 步时另一个线程处于事件状态的可能性会增加,从而导致额外的随机延迟。
现在,当涉及到很多进程时,其他因素开始发挥作用,比如内存交换(因为与线程不同,运行相同代码的进程不共享内存)(我很确定还有其他延迟来自很多进程而不是线程竞争资源但不能从我的头顶指出它)。这就是性能变得不稳定的原因。
关于python - 在执行 I/O 绑定(bind)任务时,20 个进程中的 400 个线程优于 4 个进程中的 400 个线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56272639/
我不知道该怎么做... function f1() { var x = 10; function f2(fx) { var x; x = 6;
早期绑定(bind)和后期绑定(bind)有什么区别? 最佳答案 简短的回答是,早期(或静态)绑定(bind)是指编译时绑定(bind),后期(或动态)绑定(bind)是指运行时绑定(bind)(例如
如何在 SwiftUI View 上使用 Binding(get: { }, set: { }) 自定义绑定(bind)与 @Binding 属性。我已成功使用此自定义绑定(bind)与 @State
我经常发现自己遇到问题,即控件的两个(相关)值被更新,并且两者都会触发昂贵的操作,或者控件可能会暂时处于不一致的状态。 例如,考虑一个数据绑定(bind),其中两个值 (x,y) 相互减去,最终结果用
我想通过我的 ViewModel 控制我的一个窗口的高度和宽度。 这看起来很简单。 但没有。它不起作用。 它检查 ViewModel 的 Width但不是 Height . 奇怪的是,如果我切换 W
UI5中一次性绑定(bind)和单向绑定(bind)有什么区别? 是否有任何用户特定的用例我会使用它们? 我无法从文档中获得太多信息。 最佳答案 单程 它的作用:单向数据流。模型数据的变化(例如通过
(define make (lambda (x) (lambda (y) (cons x (list y))))) (let ((x 7) (p (make 4))) (cons
尽管我或多或少地了解什么是语言绑定(bind),但我很难理解它们是如何工作的。 例如,谁能解释一下如何为 WinAPI 制作 Java 绑定(bind)? 最佳答案 如果您搜索 Foreign Fun
谁能解释为什么我可以重新绑定(bind)列表但不能+? (binding [list vector] (list 1 3)) (binding [list +] (list 1 3)) (bi
我真的很喜欢 Caliburn 和命名约定绑定(bind),我很惊讶 可见性与“CanNAME”约定用于保护 Action 的方式不同。 据我所知, BooleanToVisibilityConver
我了解动态绑定(bind)的实现方式以及静态绑定(bind)和动态绑定(bind)之间的区别,但我只是无法理解动态绑定(bind)的定义。基本上它是一种运行时绑定(bind)类型。 最佳答案 基本上,
http://jsfiddle.net/3NRsd/ var foo = $("div").bind("click", function() { $("div").animate({"hei
这个问题我快疯了...我有一个用户控件,它有一个用于插入操作的 FormView 和一个用于所有其他操作的 GridView。 在这两个控件中,我都有一个 DropDownList,如下所示: '
我有一个绑定(bind)到 ListBox 的地址的 ObservableCollection。然后在 ItemTemplate 中,我使用 {Binding .} 绑定(bind)到当前地址记录。这
如果我有以下简单的 js/knockout 代码: .js( View 模型): var image = ko.observable('http://placehold.it/300x150'); 看
我正在 aurelia 上开发一个自定义属性,让用户在输入文本区域时从列表中进行选择。例如,用法将是这样的: 正如您可能注意到的,auto-complete是属性。现在,当我想显示提示时,我想在自定
我正在使用 EventEmitter2作为我的应用程序内部的消息总线。现在我需要绑定(bind)和取消绑定(bind)一些事件处理程序。因为我也希望他们bind将它们添加到给定的上下文中,我最终得到以
我有以下函数调用: $(".selector").on("click", callback.bind(this, param1, param2)); 在我的回调函数中,我想使用绑定(bind)的 th
我目前正在试验新的编译绑定(bind),并且(再次)达到了我在拼图中遗漏了一个小问题:为什么我必须调用 Bindings.Update?直到现在,我还认为实现 INotifyPropertyChang
我正在阅读一本关于编写 JavaScript 框架的书,并找到了这段代码。但是我不明白它是如何工作的,尤其是 bind.bind 的用法?有人知道吗? var bind = Function.prot
我是一名优秀的程序员,十分优秀!