mieki256's diary



2022/05/02(月) [n年前の日記]

#1 [python][gimp] Pythonのリスト内包表記を試してみた

昨日、Pythonスクリプトのベンチマークを取りながら、ARGBのデータをRGBAに変換する処理を速くできないか試していたのだけど。

_Pythonのstructを使った時の処理時間を調べてた

高速化に関するページを眺めていたら、リスト内包表記とやらにするだけでも高速化できるという話を見かけたので試してみた。

環境は、Windows10 x64 21H2 + Python 2.7.18 32bit。CPU は AMD Ryzen 7 1700 (3GHz, 8コア16スレッド)。

ベンチマークをとってみた。スクリプトは以下。

_03_argb_rgba_conv.py
from benchmarker import Benchmarker
import struct
import sys

LOOP = 5


def create_src_data():
    print("start.")
    rgba = []
    w, h = 2048, 2048
    r, g, b, a = 0, 32, 64, 128
    for y in range(h):
        for x in range(w):
            rgba.append(struct.pack('4B', b, g, r, a))
            r += 1
            g += 1
            b += 1
            a += 1
            if r > 255:
                r = 0
            if g > 255:
                g = 0
            if b > 255:
                b = 0
            if a > 255:
                a = 0
    src = ''.join(rgba)
    print("create source data.")
    return src


def get_rgba_str_slow(src):
    buf = ""
    size = len(src)
    for i in range(size / 4):
        i0 = i * 4
        i1 = i0 + 4
        bgra = struct.unpack('=L', src[i0: i1])[0]
        a = (bgra >> 24) & 0x0ff
        r = (bgra >> 16) & 0x0ff
        g = (bgra >> 8) & 0x0ff
        b = bgra & 0x0ff
        rgba = struct.pack('4B', r, g, b, a)
        buf += rgba
    return buf


def get_rgba_str(src):
    unpack = struct.Struct('=L').unpack_from
    pack = struct.Struct('>L').pack
    lmax = len(src) / 4
    rgba = [None] * lmax
    for i in range(lmax):
        d = unpack(src, i * 4)[0]
        rgba[i] = pack(((d & 0x00ffffff) << 8) | ((d >> 24) & 0x0ff))
    return ''.join(rgba)


def get_rgba_str_b(src):
    unpack = struct.Struct('=L').unpack_from
    pack = struct.Struct('>L').pack
    lmax = len(src) / 4
    rgba = []
    for i in range(lmax):
        d = unpack(src, i * 4)[0]
        rgba.append(pack(((d & 0x00ffffff) << 8) | ((d >> 24) & 0x0ff)))
    return ''.join(rgba)


def get_rgba_str_c(src):
    lmax = len(src) / 4
    argb = list(struct.unpack("=%dL" % lmax, src))
    spack = struct.Struct('>L').pack
    rgba = [None] * lmax
    for i, d in enumerate(argb):
        rgba[i] = spack(((d & 0x0ffffff) << 8) | ((d >> 24) & 0x0ff))
    return ''.join(rgba)


def get_rgba_str_d(src):
    lmax = len(src) / 4
    argb = list(struct.unpack("=%dL" % lmax, src))
    rgba = [None] * lmax
    for i, d in enumerate(argb):
        rgba[i] = ((d & 0x0ffffff) << 8) | ((d >> 24) & 0x0ff)
    return struct.pack(">%dL" % lmax, *rgba)


def get_rgba_str_e(src):
    lmax = len(src) / 4
    argb = list(struct.unpack("=%dL" % lmax, src))
    rgba = []
    for d in argb:
        rgba.append(((d & 0x0ffffff) << 8) | ((d >> 24) & 0x0ff))
    return struct.pack(">%dL" % lmax, *rgba)


def get_rgba_str_f(src):
    lmax = len(src) / 4
    argb = list(struct.unpack("=%dL" % lmax, src))
    rgba = [(((d & 0x0ffffff) << 8) | ((d >> 24) & 0x0ff)) for d in argb]
    return struct.pack(">%dL" % lmax, *rgba)


def get_rgba_str_g(src):
    lmax = len(src) / 4
    argb = list(struct.unpack("=%dL" % lmax, src))
    rgba = [(((d & 0x0ffffff) << 8) + ((d >> 24) & 0x0ff)) for d in argb]
    return struct.pack(">%dL" % lmax, *rgba)


def check_result(src):
    dst = []
    dst.append(get_rgba_str(src))
    dst.append(get_rgba_str_slow(src))
    dst.append(get_rgba_str_b(src))
    dst.append(get_rgba_str_c(src))
    dst.append(get_rgba_str_d(src))
    dst.append(get_rgba_str_e(src))
    dst.append(get_rgba_str_f(src))
    dst.append(get_rgba_str_g(src))
    for i in range(len(dst)):
        if i == 0:
            continue
        if dst[0] == dst[i]:
            print("Success [%d]" % i)
        else:
            print("Failure [%d]" % i)


src = create_src_data()

# check_result(src)
# sys.exit()

with Benchmarker(LOOP, width=20) as bench:

    @bench("argb split")
    def check_use_split(bm):
        get_rgba_str_slow(src)

    @bench("rgb shift A")
    def check_use_rgb_shift(bm):
        get_rgba_str(src)

    @bench("rgb shift B")
    def check_use_rgb_shift_b(bm):
        get_rgba_str_b(src)

    @bench("rgb shift C")
    def check_use_rgb_shift_c(bm):
        get_rgba_str_c(src)

    @bench("rgb shift D")
    def check_use_rgb_shift_d(bm):
        get_rgba_str_d(src)

    @bench("rgb shift E")
    def check_use_rgb_shift_e(bm):
        get_rgba_str_e(src)

    @bench("rgb shift F")
    def check_use_rgb_shift_f(bm):
        get_rgba_str_f(src)

    @bench("rgb shift G")
    def check_use_rgb_shift_g(bm):
        get_rgba_str_g(src)


py -2 03_argb_rgba_conv.py で実行。結果は以下。

D:\home\prg\python\_test_sample\benchmarker> py -2 03_argb_rgba_conv.py
start.
create source data.
## benchmarker:         release 4.0.1 (for python)
## python version:      2.7.18
## python compiler:     MSC v.1500 32 bit (Intel)
## python platform:     Windows-10-10.0.19041
## python executable:   C:\Python\Python27\python.exe
## cpu model:           AMD64 Family 23 Model 1 Stepping 1, AuthenticAMD
## parameters:          loop=5, cycle=1, extra=0

##                        real    (total    = user    + sys)
argb split             16.1320   16.1094   11.1250    4.9844
rgb shift A             4.7300    4.7344    4.7031    0.0312
rgb shift B             4.8720    4.8750    4.7656    0.1094
rgb shift C             2.5780    2.5781    2.5000    0.0781
rgb shift D             2.0750    2.0625    2.0156    0.0469
rgb shift E             2.1690    2.1719    2.0000    0.1719
rgb shift F             1.7300    1.7344    1.5781    0.1562
rgb shift G             1.6780    1.6875    1.5781    0.1094

## Ranking                real
rgb shift G             1.6780  (100.0) ********************
rgb shift F             1.7300  ( 97.0) *******************
rgb shift D             2.0750  ( 80.9) ****************
rgb shift E             2.1690  ( 77.4) ***************
rgb shift C             2.5780  ( 65.1) *************
rgb shift A             4.7300  ( 35.5) *******
rgb shift B             4.8720  ( 34.4) *******
argb split             16.1320  ( 10.4) **

## Matrix                 real    [01]    [02]    [03]    [04]    [05]    [06]    [07]    [08]
[01] rgb shift G        1.6780   100.0   103.1   123.7   129.3   153.6   281.9   290.3   961.4
[02] rgb shift F        1.7300    97.0   100.0   119.9   125.4   149.0   273.4   281.6   932.5
[03] rgb shift D        2.0750    80.9    83.4   100.0   104.5   124.2   228.0   234.8   777.4
[04] rgb shift E        2.1690    77.4    79.8    95.7   100.0   118.9   218.1   224.6   743.8
[05] rgb shift C        2.5780    65.1    67.1    80.5    84.1   100.0   183.5   189.0   625.8
[06] rgb shift A        4.7300    35.5    36.6    43.9    45.9    54.5   100.0   103.0   341.1
[07] rgb shift B        4.8720    34.4    35.5    42.6    44.5    52.9    97.1   100.0   331.1
[08] argb split        16.1320    10.4    10.7    12.9    13.4    16.0    29.3    30.2   100.0


「rgb shift F」「rgb shift G」が、リスト内包表記を使った版。たしかに、2.0750秒が、1.7300秒になった。リスト内包表記にするだけでも高速化はできるらしい。

また、論理和(OR)演算子(|)を使っていたところを、+演算子に変えてみたところ、それだけでもほんのちょっとだけ処理が速くなった。ビット演算は微妙に遅いということだろうか…?

つまるところ、以下のように変更したことで、ある程度高速化できた、ということになるのかなと。

def get_rgba_str(src):
    unpack = struct.Struct('=L').unpack_from
    pack = struct.Struct('>L').pack
    lmax = len(src) / 4
    rgba = [None] * lmax
    for i in range(lmax):
        d = unpack(src, i * 4)[0]
        rgba[i] = pack(((d & 0x00ffffff) << 8) | ((d >> 24) & 0x0ff))
    return ''.join(rgba)
def get_rgba_str_g(src):
    lmax = len(src) / 4
    argb = list(struct.unpack("=%dL" % lmax, src))
    rgba = [(((d & 0x0ffffff) << 8) + ((d >> 24) & 0x0ff)) for d in argb]
    return struct.pack(">%dL" % lmax, *rgba)


ただ、これは GIMP の Python-Fu に限定した話だけど、少々問題もあって…。リスト内包表記にしたことで、GIMPのプログレスバーを更新して処理の進行具合をユーザに伝えることができなくなってしまった。今まではforループで処理してたから、forループ内でプログレスバーを更新する処理を呼ぶことができていたのだけど…。でもまあ、5秒近くかかっていた処理が1.7秒になるのだから、プログレスバーを更新しなくても許されるかなと…。

さておき。もっと高速化できないものだろうか…。

スクリプトを動かしている環境がビッグエンディアンなのかリトルエンディアンなのか分からないので、struct.unpack("=L", xxx) を使って処理をしているけれど、どちらのエンディアンなのか事前に判別すれば、struct を使わなくても済むのではないか…? データの並びが BGRABGRA...、あるいは ARGBARGB... になっているものと決め打ちできれば、もっとシンプルに書けて高速化できないか。

以上です。

過去ログ表示

Prev - 2022/05 - Next
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project