Создание чередованного буфера для pyopengl и numpy

Я пытаюсь обработать в пакетном режиме набор вершин и проводов структуры в чередованном массиве прежде, чем отправить его в glInterleavedArrays/glDrawArrays pyOpengl. Единственная проблема состоит в том, что я не могу найти соответственно достаточно быстрый способ добавить данные в массив numpy.

Существует ли лучший способ сделать это? Я думал бы, что это будет более быстро для предварительного выделения массива, и затем заполниться это данными, но вместо этого, генерируя список Python и преобразовывая его в массив numpy "быстрее". Хотя 15 мс для 4 096 четверок кажутся медленными.

Я включал некоторый пример кода и их синхронизации.

#!/usr/bin/python

import timeit
import numpy
import ctypes
import random

USE_RANDOM=True
USE_STATIC_BUFFER=True

STATIC_BUFFER = numpy.empty(4096*20, dtype=numpy.float32)

def render(i):
    # pretend these are different each time
    if USE_RANDOM:
        tex_left, tex_right, tex_top, tex_bottom = random.random(), random.random(), random.random(), random.random()
        left, right, top, bottom = random.random(), random.random(), random.random(), random.random()
    else:
        tex_left, tex_right, tex_top, tex_bottom = 0.0, 1.0, 1.0, 0.0
        left, right, top, bottom = -1.0, 1.0, 1.0, -1.0

    ibuffer = (
        tex_left, tex_bottom,   left, bottom, 0.0,  # Lower left corner
        tex_right, tex_bottom,  right, bottom, 0.0, # Lower right corner
        tex_right, tex_top,     right, top, 0.0,    # Upper right corner
        tex_left, tex_top,      left, top, 0.0,     # upper left
    )

    return ibuffer



# create python list.. convert to numpy array at end
def create_array_1():
    ibuffer = []
    for x in xrange(4096):
        data = render(x)
        ibuffer += data

    ibuffer = numpy.array(ibuffer, dtype=numpy.float32)
    return ibuffer

# numpy.array, placing individually by index
def create_array_2():
    if USE_STATIC_BUFFER:
        ibuffer = STATIC_BUFFER
    else:
        ibuffer = numpy.empty(4096*20, dtype=numpy.float32)
    index = 0
    for x in xrange(4096):
        data = render(x)
        for v in data:
            ibuffer[index] = v
            index += 1
    return ibuffer

# using slicing
def create_array_3():
    if USE_STATIC_BUFFER:
        ibuffer = STATIC_BUFFER
    else:
        ibuffer = numpy.empty(4096*20, dtype=numpy.float32)
    index = 0
    for x in xrange(4096):
        data = render(x)
        ibuffer[index:index+20] = data
        index += 20
    return ibuffer

# using numpy.concat on a list of ibuffers
def create_array_4():
    ibuffer_concat = []
    for x in xrange(4096):
        data = render(x)
        # converting makes a diff!
        data = numpy.array(data, dtype=numpy.float32)
        ibuffer_concat.append(data)
    return numpy.concatenate(ibuffer_concat)

# using numpy array.put
def create_array_5():
    if USE_STATIC_BUFFER:
        ibuffer = STATIC_BUFFER
    else:
        ibuffer = numpy.empty(4096*20, dtype=numpy.float32)
    index = 0
    for x in xrange(4096):
        data = render(x)
        ibuffer.put( xrange(index, index+20), data)
        index += 20
    return ibuffer

# using ctype array
CTYPES_ARRAY = ctypes.c_float*(4096*20)
def create_array_6():
    ibuffer = []
    for x in xrange(4096):
        data = render(x)
        ibuffer += data
    ibuffer = CTYPES_ARRAY(*ibuffer)
    return ibuffer

def equals(a, b):

    for i,v in enumerate(a):
        if b[i] != v:
            return False
    return True



if __name__ == "__main__":
    number = 100

    # if random, don't try and compare arrays
    if not USE_RANDOM and not USE_STATIC_BUFFER:
        a =  create_array_1()
        assert equals( a, create_array_2() )
        assert equals( a, create_array_3() )
        assert equals( a, create_array_4() )
        assert equals( a, create_array_5() )
        assert equals( a, create_array_6() )

    t = timeit.Timer( "testing2.create_array_1()", "import testing2" )
    print 'from list:', t.timeit(number)/number*1000.0, 'ms'

    t = timeit.Timer( "testing2.create_array_2()", "import testing2" )
    print 'array: indexed:', t.timeit(number)/number*1000.0, 'ms'

    t = timeit.Timer( "testing2.create_array_3()", "import testing2" )
    print 'array: slicing:', t.timeit(number)/number*1000.0, 'ms'

    t = timeit.Timer( "testing2.create_array_4()", "import testing2" )
    print 'array: concat:', t.timeit(number)/number*1000.0, 'ms'

    t = timeit.Timer( "testing2.create_array_5()", "import testing2" )
    print 'array: put:', t.timeit(number)/number*1000.0, 'ms'

    t = timeit.Timer( "testing2.create_array_6()", "import testing2" )
    print 'ctypes float array:', t.timeit(number)/number*1000.0, 'ms'

Синхронизации с помощью случайных чисел:

$ python testing2.py
from list: 15.0486779213 ms
array: indexed: 24.8184704781 ms
array: slicing: 50.2214789391 ms
array: concat: 44.1691994667 ms
array: put: 73.5879898071 ms
ctypes float array: 20.6674289703 ms

примечание редактирования: измененный код для создания случайных чисел для каждого рендеринга, чтобы уменьшить объектное повторное использование и моделировать различные вершины каждый раз.

редактирование note2: добавленный статический буфер и сила весь numpy.empty () для использования dtype=float32

примечание 1/Apr/2010: все еще никакой прогресс и я действительно не чувствуем, что любой из ответов решил проблему все же.

7
задан Nick Sonneveld 1 April 2010 в 04:48
поделиться

3 ответа

Я знаю, это кажется странным, но пробовали ли вы из файла ?

0
ответ дан 7 December 2019 в 18:42
поделиться

Преимущество numpy не реализуется простым хранением данных в массиве, оно достигается путем выполнения операций над многими элементами в массиве вместо одного за другим. Ваш пример можно упростить и оптимизировать до этого тривиального решения с ускорением на несколько порядков:

numpy.random.standard_normal(4096*20)

... это не очень полезно, но дает намек на то, где находятся затраты.

Это постепенное улучшение, которое превосходит решение добавления списка (но незначительно) за счет исключения итерации по 4096 элементам.

xs = numpy.arange(4096)
render2 = numpy.vectorize(render)

def create_array_7():
    ibuffer = STATIC_BUFFER
    for i, a in enumerate(render2(xs)):
        ibuffer[i::20] = a
    return ibuffer

... но не то ускорение, которое мы ищем.

Реальная экономия будет достигнута за счет переделки процедуры рендеринга, так что вам не нужно создавать объект python для каждого значения, которое в конечном итоге помещается в буфер. Где tex_left, tex_right ... и т. Д. родом из? Они вычисляются или читаются?

1
ответ дан 7 December 2019 в 18:42
поделиться

Причина, по которой create_array_1 работает намного быстрее, заключается в том, что все элементы в списке (python) указывают на один и тот же объект. Это можно увидеть, если проверить:

print (ibuffer[0] is ibuffer[1])

внутри подпрограмм. В create_array_1 это истинно (до создания массива numpy), в то время как в create_array_2 это всегда будет false. Я предполагаю, что это означает, что шаг преобразования данных в преобразовании массива должен произойти только один раз в create_array_1, в то время как в create_array_2 он происходит 4096 раз.

Если причина в этом, то я полагаю, что тайминги будут другими, если заставить рендер генерировать случайные данные. Create_array_5 - самый медленный, поскольку он создает новый массив каждый раз, когда вы добавляете данные в конец.

1
ответ дан 7 December 2019 в 18:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: