pyav 拆幀速度和執行緒數目的關係測試

ponponon發表於2023-04-03

前言

本次測試,關注兩個點:

  • 執行緒數目和拆幀速度的關係
  • 不同解析度的影片,對拆幀速度的影響

程式碼 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

執行緒數耗時(秒)倍速
812.1747.3
712.5245.9
613.2743.3
514.4839.7
416.9533.9
321.5826.6
228.5820.1
146.312.4
該測試,沒有做資源限制,直接跑在 macbook 上,沒有使用虛擬機器或者 docker 限制 cpu 或者記憶體資源。屬於撒開丫子跑

平臺 Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz

執行緒數耗時(秒)倍速
1020.9827.4
920.9827.4
821.526.7
721.3926.9
623.8224.1
524.8523.1
430.0319.1
337.9615.1
250.0811.5
174.487.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

執行緒數耗時(秒)倍速
84.81119.5
74.72121.7
64.81119.5
54.96116.0
46.6586.4
38.1670.5
29.9857.6
114.6839.2

平臺 Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz

執行緒數耗時(秒)倍速
1011.251.3
911.5949.6
812.4746.1
713.7941.7
612.3846.4
58.4967.7
414.9238.6
313.0244.2
217.0433.7
123.824.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

執行緒數耗時(秒)倍速
1099.715.8
995.686.0
895.026.1
792.036.2
691.686.3
588.566.5
487.96.5
385.736.7
284.116.8
175.167.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

執行緒數耗時(秒)倍速
1029.6119.4
928.5620.1
828.5620.1
727.8420.7
627.8220.7
527.4720.9
428.1320.4
327.2121.1
227.0621.2
123.924.1
測試的時候,vagrant+virtualbox,限定 cpu 個數為 1

可以看到,多執行緒會拖後腿,10個執行緒比一個執行緒慢了 20%!!!

總結

如果你的程式跑在 k8s 或者 docker 容器中,並且使用了 cgroup 做 CPU 資源限制,那麼我建議你,也要合理主動設定 pyav 的執行緒數

因為 pyav 預設的執行緒數,是按照你機器的 cpu 核來的。比如你的機器有 100 核,但是你把程式跑在 docker 容器裡面,並且限制 cpu 資源上限就是一核,但是 pyav 認為我可以用 100 核,就會設定很多個執行緒。但實際只能最多用一核,這反而變慢了

相關文章