簡體   English   中英

Python RLock IO綁定?

[英]Python RLock IO-Bound?

我有一組受CPU約束的進程,只要它們的唯一同步使作業脫離隊列,它們就可以將任意數量的內核都達到100%的利用率。

一旦添加RLock以避免在更新文件系統中的目錄時出現最壞情況,則CPU /核心利用率下降到60%,就像進程已成為IO綁定一樣。

有什么解釋?

這與整體速度無關。 它與CPU /內核利用率有關,因此Python 2/3,Cython或PyPy無關緊要。

更新:我對自己的問題作了部分回答。 對於我的特殊情況,最終的解決方案包括修改文件系統的訪問方式,因此不需要同步(“映射/縮小”的一種)。

這完全取決於multiprocessing如何實現RLock 我知道多處理可以跨主機工作,這意味着同步原語可以跨套接字工作。 如果是這樣,它將引入很多(可變)延遲。

所以我做了一個實驗。

下面是一個例子諾迪RLock正在使用的一個以上的過程(以防止任何的快速路徑,其中所有鎖都在同一進程內):

#!/usr/bin/env python
import multiprocessing
from time import sleep

lock = multiprocessing.RLock()

def noop(myname):
    # nonlocal lock
    sleep(0.5)
    print myname, "acquiring lock"
    with lock:
        print myname, "has lock"
        sleep(0.5)
    print myname, "released lock"

sProc1 = multiprocessing.Process(target=noop, args=('alice',))
sProc2 = multiprocessing.Process(target=noop, args=('bob',))

sProc1.start()
sProc2.start()

sProc1.join()
sProc2.join()

運行此命令時,其輸出如下所示:

alice acquiring lock
alice has lock
bob acquiring lock
alice released lock
bob has lock
bob released lock

太好了,現在通過strace進行系統調用跟蹤來運行它。

在下面的命令中, -ff選項告訴工具“跟隨fork() ”調用,即跟蹤由主進程啟動的任何進程。 為了簡潔起見,我還使用-e trace=futex,write ,它根據我在發布此結論之前得出的結論過濾輸出。 通常,您將在不使用-e選項的情況下運行,並使用文本編輯器/ grep探索事后發生的情況。

# strace -ff -e trace=futex,write ./traceme.py
futex(0x7fffeafe29bc, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, NULL, 7fb92ac6c700) = -1 EAGAIN (Resource temporarily unavailable)
futex(0x7fb92a8540b0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
futex(0x7fb92aa7131c, FUTEX_WAKE_PRIVATE, 2147483647) = 0
write(3, "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 32) = 32
Process 25873 attached
Process 25874 attached
Process 25872 suspended
[pid 25873] write(1, "alice acquiring lock\n", 21alice acquiring lock
) = 21
[pid 25873] write(1, "alice has lock\n", 15alice has lock
) = 15
[pid 25874] write(1, "bob acquiring lock\n", 19bob acquiring lock
) = 19
[pid 25874] futex(0x7fb92ac91000, FUTEX_WAIT, 0, NULL <unfinished ...>
[pid 25873] futex(0x7fb92ac91000, FUTEX_WAKE, 1 <unfinished ...>
[pid 25874] <... futex resumed> )       = 0
[pid 25873] <... futex resumed> )       = 1
[pid 25874] write(1, "bob has lock\n", 13 <unfinished ...>
bob has lock
[pid 25873] write(1, "alice released lock\n", 20 <unfinished ...>
alice released lock
[pid 25874] <... write resumed> )       = 13
[pid 25873] <... write resumed> )       = 20
Process 25872 resumed
Process 25873 detached
[pid 25872] --- SIGCHLD (Child exited) @ 0 (0) ---
Process 25872 suspended
[pid 25874] write(1, "bob released lock\n", 18bob released lock
) = 18
Process 25872 resumed
Process 25874 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

從阻塞並稍后恢復的print( write() )消息和futex調用的模式來看,很明顯, RLock是使用futex或“ Fast Userspace Mutex”實現的。 顧名思義,這是同步的不錯選擇。

當某個進程在諸如futex之類的系統調用中被阻塞時,該進程出於所有意圖和目的都將阻塞I / O。

所有這些都意味着multiprocessing.RLock是高效的,並且可以執行其設計要完成的工作。 因此,如果您的應用程序的性能低於使用同步時的預期性能,則可能是您的算法受到了指責。

測量表明RLock不受I / O限制。 盡管與RLock (或Semaphore )同步的代碼似乎比沒有同步的代碼慢大約6倍,但是執行最簡單的I / O的代碼要慢幾個數量級。

下面是我用來測量Python和PyPy的RLock開銷的單進程代碼。 我仍然不明白為什么RLock的開銷在單個進程中如此之高,或者為什么該開銷不能維持CPU /核心利用率,但是結果表明,使用標准同步原語比嘗試滾動您的RLock更有效。擁有。

# lock.py
import sys
import os
import timeit
from random import random
from multiprocessing import RLock, Semaphore

N=8*1024*1024

lock = RLock()
semaphore = Semaphore()

def touch(fname, times=None):
    if not os.path.isfile(fname):
        open(fname, 'wa').close()
    else:
        with file(fname, 'a'):
            os.utime(fname, times)

def wolock():
    return random()

def wlock():
    with lock:
        return random()

def wsem():
    with semaphore:
        return random()

def wfile():
   os.path.isfile('lock')
   touch('lock') 
   try:
        return random()
   finally:
        os.unlink('lock')

def run(get):
    result = []
    for i in xrange(N):
        result.append(get())
    return result

def main():
    t0 = timeit.timeit('lock.wolock()', setup='import lock', number=N)
    print '%8.3f %8.2f%% %s' % (t0, 100, 'unsynchronized')
    t1 = timeit.timeit('lock.wlock()', setup='import lock', number=N)
    print '%8.3f %8.2f%% %s' % (t1, 100*t1/t0, 'rlock')
    t2 = timeit.timeit('lock.wsem()', setup='import lock', number=N)
    print '%8.3f %8.2f%% %s' % (t2, 100*t2/t0, 'semaphore')
    t = timeit.timeit('lock.wfile()', setup='import lock', number=N)
    print '%8.3f %s' % (t, 'file system')

if __name__ == '__main__':
    main()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM