前言
本次測試,關注兩個點:
- 執行緒數目和拆幀速度的關係
- 不同解析度的影片,對拆幀速度的影響
程式碼 demo
測試程式碼
import av
import time
from pathlib import Path
input_file = '/home/vobile/Downloads/XiaoShengKeDeJiuShu_4-1080P.mp4'
def get_video_seconds(video_file_path: Path) -> int:
import av
with av.open(str(video_file_path), metadata_encoding='utf-8', metadata_errors='ignore') as container:
stream = container.streams.video[0]
return int(stream.frames/stream.average_rate)
total_seconds: int = get_video_seconds(Path(input_file))
items = []
for thread_count in reversed([1,2,3,4,5,6,7,8,9,10]):
count = 0
s = time.time()
with av.open(input_file, metadata_encoding='utf-8', metadata_errors='ignore') as container:
video_stream = container.streams.video[0]
video_stream.thread_type = 'AUTO'
video_stream.thread_count = thread_count
average_fps: int = round(video_stream.average_rate)
interval = 1
for index, frame in enumerate(container.decode(video_stream)):
if index % (average_fps) == 0:
frame.to_ndarray(format='rgb24')
count += 1
e = time.time()
print(f'thread count is {thread_count}, pay time is {e-s}')
items.append([thread_count, round(e-s, 2), round(total_seconds/(e-s), 1)])
for item in items:
print('| ', ' | '.join([str(i) for i in item]), ' |')
上面的程式碼,使用 pyav,按照一秒一幀的方式,從影片中提取幀
不同解析度的影片
高解析度影片
重新測試,加上倍速
影片是一個 1080P 的影片
╰─➤ ffmpeg -i /Volumes/SanDisk128G/標準影片/XiaoShengKeDeJiuShu_4-1080P.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/Volumes/SanDisk128G/標準影片/XiaoShengKeDeJiuShu_4-1080P.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf57.71.100
description : Packed by Bilibili XCoder v2.0.2
Duration: 00:09:35.04, start: 0.000000, bitrate: 2832 kb/s
Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 2631 kb/s, 30 fps, 30 tbr, 16k tbn (default)
Metadata:
handler_name : VideoHandler
vendor_id : [0][0][0][0]
Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 192 kb/s (default)
Metadata:
handler_name : SoundHandler
vendor_id : [0][0][0][0]
At least one output file must be specified
平臺 macbook pro Apple Silicon M1
執行緒數 | 耗時(秒) | 倍速 |
---|---|---|
8 | 12.17 | 47.3 |
7 | 12.52 | 45.9 |
6 | 13.27 | 43.3 |
5 | 14.48 | 39.7 |
4 | 16.95 | 33.9 |
3 | 21.58 | 26.6 |
2 | 28.58 | 20.1 |
1 | 46.3 | 12.4 |
該測試,沒有做資源限制,直接跑在 macbook 上,沒有使用虛擬機器或者 docker 限制 cpu 或者記憶體資源。屬於撒開丫子跑
平臺 Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz
執行緒數 | 耗時(秒) | 倍速 |
---|---|---|
10 | 20.98 | 27.4 |
9 | 20.98 | 27.4 |
8 | 21.5 | 26.7 |
7 | 21.39 | 26.9 |
6 | 23.82 | 24.1 |
5 | 24.85 | 23.1 |
4 | 30.03 | 19.1 |
3 | 37.96 | 15.1 |
2 | 50.08 | 11.5 |
1 | 74.48 | 7.7 |
該測試,沒有做資源限制,直接跑在 Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz 上,沒有使用虛擬機器或者 docker 限制 cpu 或者記憶體資源。屬於撒開丫子跑。
Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz 有 28 個物理核,56 個邏輯核,都可以被程式呼叫。當然,ffmpeg、pyav 不會有多少吃多少,我記得是上限最多 16 個執行緒。
低解析度影片
再換一個低解析度的影片
影片是一個 540P 的影片
╰─➤ ffmpeg -i /home/vobile/Downloads/XiaoShengKeDeJiuShu_4-540P.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/vobile/Downloads/XiaoShengKeDeJiuShu_4-540P.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf59.27.100
description : Packed by Bilibili XCoder v2.0.2
Duration: 00:09:35.04, start: 0.000000, bitrate: 1049 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 960x540 [SAR 1:1 DAR 16:9], 849 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)
Metadata:
handler_name : VideoHandler
vendor_id : [0][0][0][0]
encoder : Lavc59.37.100 libx264
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 192 kb/s (default)
Metadata:
handler_name : SoundHandler
vendor_id : [0][0][0][0]
At least one output file must be specified
平臺 macbook pro Apple Silicon M1
執行緒數 | 耗時(秒) | 倍速 |
---|---|---|
8 | 4.81 | 119.5 |
7 | 4.72 | 121.7 |
6 | 4.81 | 119.5 |
5 | 4.96 | 116.0 |
4 | 6.65 | 86.4 |
3 | 8.16 | 70.5 |
2 | 9.98 | 57.6 |
1 | 14.68 | 39.2 |
平臺 Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz
執行緒數 | 耗時(秒) | 倍速 |
---|---|---|
10 | 11.2 | 51.3 |
9 | 11.59 | 49.6 |
8 | 12.47 | 46.1 |
7 | 13.79 | 41.7 |
6 | 12.38 | 46.4 |
5 | 8.49 | 67.7 |
4 | 14.92 | 38.6 |
3 | 13.02 | 44.2 |
2 | 17.04 | 33.7 |
1 | 23.8 | 24.2 |
總結
結論一:多執行緒可以加速拆幀,但是不是線性相關。隨著執行緒數的增加,倍速提升越小
結論二:解析度越高的影片,多執行緒加速效果越明顯
對於高解析度的影片,8 個執行緒,相比 1 個執行緒,快了 4 倍
對於低解析度的影片,8 個執行緒,相比 1 個執行緒,快了 2 倍
多執行緒會拖後腿嗎?
如果機器只有一個 cpu core,此時,pyav 啟用多執行緒拆幀,會扯後腿嗎?(就是多執行緒比單執行緒還慢)
我想測試一下,所以使用 vagrant+virtualbox,限定 cpu 個數為 1
高解析度影片
還是剛剛的 1080P 的影片
╰─➤ ffmpeg -i /Volumes/SanDisk128G/標準影片/XiaoShengKeDeJiuShu_4-1080P.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/Volumes/SanDisk128G/標準影片/XiaoShengKeDeJiuShu_4-1080P.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf57.71.100
description : Packed by Bilibili XCoder v2.0.2
Duration: 00:09:35.04, start: 0.000000, bitrate: 2832 kb/s
Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 2631 kb/s, 30 fps, 30 tbr, 16k tbn (default)
Metadata:
handler_name : VideoHandler
vendor_id : [0][0][0][0]
Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 192 kb/s (default)
Metadata:
handler_name : SoundHandler
vendor_id : [0][0][0][0]
At least one output file must be specified
平臺 Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz,開一個 cpu core
執行緒數 | 耗時(秒) | 倍速 |
---|---|---|
10 | 99.71 | 5.8 |
9 | 95.68 | 6.0 |
8 | 95.02 | 6.1 |
7 | 92.03 | 6.2 |
6 | 91.68 | 6.3 |
5 | 88.56 | 6.5 |
4 | 87.9 | 6.5 |
3 | 85.73 | 6.7 |
2 | 84.11 | 6.8 |
1 | 75.16 | 7.7 |
測試的時候,vagrant+virtualbox,限定 cpu 個數為 1
可以看到,多執行緒會拖後腿,10個執行緒比一個執行緒慢了 25%!!!
低解析度影片
影片還是選用這個
╰─➤ ffmpeg -i /home/vobile/Downloads/XiaoShengKeDeJiuShu_4-540P.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/vobile/Downloads/XiaoShengKeDeJiuShu_4-540P.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf59.27.100
description : Packed by Bilibili XCoder v2.0.2
Duration: 00:09:35.04, start: 0.000000, bitrate: 1049 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 960x540 [SAR 1:1 DAR 16:9], 849 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)
Metadata:
handler_name : VideoHandler
vendor_id : [0][0][0][0]
encoder : Lavc59.37.100 libx264
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 192 kb/s (default)
Metadata:
handler_name : SoundHandler
vendor_id : [0][0][0][0]
At least one output file must be specified
平臺 Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz,開一個 cpu core
執行緒數 | 耗時(秒) | 倍速 |
---|---|---|
10 | 29.61 | 19.4 |
9 | 28.56 | 20.1 |
8 | 28.56 | 20.1 |
7 | 27.84 | 20.7 |
6 | 27.82 | 20.7 |
5 | 27.47 | 20.9 |
4 | 28.13 | 20.4 |
3 | 27.21 | 21.1 |
2 | 27.06 | 21.2 |
1 | 23.9 | 24.1 |
測試的時候,vagrant+virtualbox,限定 cpu 個數為 1
可以看到,多執行緒會拖後腿,10個執行緒比一個執行緒慢了 20%!!!
總結
如果你的程式跑在 k8s 或者 docker 容器中,並且使用了 cgroup 做 CPU 資源限制,那麼我建議你,也要合理主動設定 pyav 的執行緒數
因為 pyav 預設的執行緒數,是按照你機器的 cpu 核來的。比如你的機器有 100 核,但是你把程式跑在 docker 容器裡面,並且限制 cpu 資源上限就是一核,但是 pyav 認為我可以用 100 核,就會設定很多個執行緒。但實際只能最多用一核,這反而變慢了