(我在这里设计并实现了有问题的 API。)
没错,Python 对象在传入和传出函数时不会被复制。从您的 CustomFactor 返回一行和将值写入提供的数组之间存在差异的原因与调用您的 CustomFactor 计算方法的代码中生成的副本有关。
最初设计 CustomFactor API 时,调用计算方法的代码大致如下所示:
def _compute(self, windows, dates, assets):
# `windows` here is list of iterators yielding 2D slices of
# the user's requested inputs
# `dates` and `assets` are row/column labels for the final output.
# Allocate a (dates x assets) output array.
# Each invocation of the user's `compute` function
# corresponds to one row of output.
output = allocate_output()
for i in range(len(dates)):
# Grab the next set of input arrays.
inputs = [next(w) for w in windows]
# Call the user's compute, which is responsible for writing
# values into `out`.
self.compute(
dates[i],
assets,
# This index is a non-copying operation.
# It creates a view into row `i` of `output`.
output[i],
*inputs # Unpack all the inputs.
)
return output
这里的基本思想是我们已经预取了大量的数据,现在我们将在窗口中循环到该数据中,在数据上调用用户的计算函数,并将结果写入预分配的输出数组,然后将其传递给进一步的转换。
无论我们做什么,我们都必须付出至少一份副本的成本,才能将用户compute
函数的结果输入到输出数组中。
正如您所指出的,最明显的 API 是让用户简单地返回输出行,在这种情况下,调用代码如下所示:
# Get the result row from the user.
result_row = self.compute(dates[i], assets, *inputs)
# Copy the user's result into our output buffer.
output[i] = result_row
如果那是 API,那么我们必须为每次调用用户的compute
- 分配用户将返回的 ~64000 字节数组。
- 用户计算数据到用户输出数组的副本。
- 从用户的输出数组复制到我们自己的更大的数组中。
使用现有的 API,我们避免了成本 (1) 和 (3)。
尽管如此,我们已经对 CustomFactors 的工作方式进行了更改,从而使上述一些优化变得不那么有用。特别是,我们现在只将数据传递给compute
当天没有被屏蔽的资产,这需要在调用之前和之后输出数组的部分副本compute
。
尽管如此,仍然有一些设计原因更喜欢现有的 API。特别是,让引擎控制输出分配使我们更容易做一些事情,比如为多输出因子传递重新数组。