Используйте Typed memoryview в cython, если размеры неизвестны

Я хочу использовать типизированное представление памяти для оптимизации функции, но не знаю, какой тип аргумента. Это может быть массив numpy или даже скаляр. Как мне тогда использовать типизированную память?


person bewithaman    schedule 07.04.2015    source источник
comment
этот вопрос звучит примерно так: I want to use static typing, but I don't know the type at compile time   -  person cel    schedule 07.04.2015
comment
@cel Да. Это звучит так же.   -  person bewithaman    schedule 07.04.2015
comment
Я думаю, что cel, возможно, намекал на то, что ваши два требования противоречат друг другу. Однако одним из подходов может быть: 1) определить функцию реализации, которая работает с одномерным представлением памяти. 2) Определите функцию-оболочку, которая работает с любым объектом Python. а) Если передано одномерное представление памяти, вызовите функцию реализации; б) если ему передан скаляр, создайте массив 1x1 и вызовите функцию реализации; c) Если ему передан массив multi-D, то либо сгладьте его для функции реализации, либо выполните итерацию по строкам, вызывая функцию реализации для каждой строки.   -  person DavidW    schedule 07.04.2015
comment
В качестве альтернативы вы можете посмотреть, как сделать numpy ufuncs в Cython, которые, вероятно, делают то, что вы хотите. Тем не менее, это потенциально довольно сложно.   -  person DavidW    schedule 07.04.2015
comment
@DavidW для меня вы могли бы добавить свой комментарий в качестве ответа ... это в значительной степени решает потребности ОП.   -  person Saullo G. P. Castro    schedule 03.05.2015
comment
@SaulloCastro В то время я не публиковал это как ответ, потому что не хотел писать код, чтобы проиллюстрировать его (и я думаю, что для правильного ответа, вероятно, нужен код). Хотя я выложил это сейчас.   -  person DavidW    schedule 05.05.2015


Ответы (1)


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

  1. определить функцию «реализации», которая работает с одномерным представлением памяти.
  2. Define a wrapper function that operates on any python object.
    1. If it's passed a 1D memoryview, call the implementation function;
    2. если ему передан скаляр, создайте массив 1x1 и вызовите функцию реализации;
    3. Если ему передан массив multi-D, то либо сгладьте его для функции реализации, либо выполните итерацию по строкам, вызывая функцию реализации для каждой строки.

Далее следует быстрая реализация. Это предполагает, что вы хотите, чтобы функция применялась к каждому элементу входного массива (и вам нужен выходной массив того же размера). Выбранная иллюстративная функция просто добавляет 1 к каждому значению. Он также использует numpy местами (а не просто типизированные представления памяти), что я считаю разумным:

cimport cython
import numpy as np
import numbers

@cython.boundscheck(False)
cdef double[:] _plus_one_impl(double[:] x):
  cdef int n
  cdef double[:] output

  output = x.copy()
  for n in range(x.shape[0]):
    output[n] = x[n]+1
  return output

def plus_one(x):
  if isinstance(x,numbers.Real): # check if it's a number
    return _plus_one_impl(np.array([x]))[0]
  else:
    try:
      return _plus_one_impl(x)
    except ValueError: # this gets thrown if conversion fails
      if len(x.shape)<2:
        raise ValueError('x could not be converted to double [:]')
      output = np.empty_like(x) # output is all numpy, whatever the input is
      for n in range(x.shape[0]): # this loop isn't typed, so is likely to be pretty slow
        output[n,...] = plus_one(x[n,...])
      return output

Этот код, вероятно, в некоторых случаях будет несколько медленным (например, двумерный массив с коротким вторым измерением).

Однако моя настоящая рекомендация — изучить ufuncs numpy, которые предоставляют интерфейс для эффективного достижения такого рода вещей. (См. http://docs.scipy.org/doc/numpy-dev/user/c-info.ufunc-tutorial.html). К сожалению, они сложнее, чем Cython.

person DavidW    schedule 05.05.2015