- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我为 numpy 库编写了一个 c 扩展,用于计算特定类型的 bincount。由于缺乏更好的名称,我们将其称为 fast_compiled
并将方法签名放置在 array_module_methods 内的 numpy/core/src/multiarray/multiarraymodule.c 中
:
{"fast_compiled", (PyCFunction)arr_fast_compiled,
METH_VARARGS | METH_KEYWORDS, NULL},
以及numpy/core/src/multiarray/compiled_base.c(和compiled_base.h)内的实际实现:
NPY_NO_EXPORT PyObject *
arr_fast_compiled(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
{
PyObject *list_obj = NULL, *strides_obj = Py_None;
PyArrayObject *list_arr = NULL, *ans = NULL, *strides_arr = NULL;
npy_intp len, ans_size, total_size;
npy_intp i, j, k;
double *dans, *weights;
npy_intp* strides;
static char *kwlist[] = {"weights", "strides", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O",
kwlist, &list_obj, &strides_obj)) {
goto fail;
}
list_arr = (PyArrayObject *)PyArray_ContiguousFromAny(list_obj, NPY_DOUBLE, 2, 2);
if (list_arr == NULL) {
goto fail;
}
len = PyArray_DIM(list_arr, 0);
weights = (double *)PyArray_DATA(list_arr);
ans_size = 2*len-1;
ans = (PyArrayObject *)PyArray_ZEROS(1, &ans_size, NPY_DOUBLE, 0);
if (ans == NULL) {
goto fail;
}
dans = (double *)PyArray_DATA(ans);
NPY_BEGIN_ALLOW_THREADS;
if (strides_obj == Py_None) {
for (i = 0; i < len; ++i) {
k = i * len;
for (j = i; j < i + len; ++j, ++k) {
dans[j] += weights[k];
}
}
Py_DECREF(list_arr);
}
else {
total_size = len*len;
strides_arr = (PyArrayObject *)PyArray_ContiguousFromAny(
strides_obj, NPY_INTP, 1, 1);
strides = (npy_intp *)PyArray_DATA(strides_arr);
for (i = 0; i < total_size; ++i) {
dans[strides[i]] += weights[i];
}
Py_DECREF(list_arr);
Py_DECREF(strides_arr);
}
NPY_END_ALLOW_THREADS;
return (PyObject *)ans;
fail:
Py_XDECREF(list_arr);
Py_XDECREF(strides_arr);
Py_XDECREF(ans);
return NULL;
}
该方法采用一个必需的位置参数 weights
和一个可选的关键字参数 strides
。根据是否指定了 strides
,它将使用不同(等效)的方式来计算答案。
我很好奇为什么预先计算步幅并将其指定为关键字参数比在嵌套 for 循环中计算步幅要慢。 IE。这是为什么:
for (i = 0; i < total_size; ++i) {
dans[strides[i]] += weights[i];
}
比这慢:
for (i = 0; i < len; ++i) {
k = i * len;
for (j = i; j < i + len; ++j, ++k) {
dans[j] += weights[k];
}
}
以下是我计算基准的方法:
import numpy as np
import perfplot
def fast_compiled(args):
A, _ = args
return np.fast_compiled(A)
def fast_compiled_strides(args):
A, strides = args
return np.fast_compiled(A, strides=strides)
def setup(n):
A = np.random.normal(size=(n, n))
strides = np.arange(n*n)
strides = np.lib.stride_tricks.sliding_window_view(strides, (n,))
strides = strides[:n]
strides = strides.flatten() # make sure it is continous
return A, strides
perfplot.show(
setup=setup,
kernels=[fast_compiled, fast_compiled_strides],
n_range=[2 ** k for k in range(3, 15)],
xlabel='n',
relative_to=0,
)
最佳答案
fast_compiled
比 fast_compiled_strides
快因为它适用于编译时已知的连续数据,使编译器能够使用 SIMD 指令(例如,通常在类 x86 平台上使用 SSE,或者在 ARM 平台上使用 NEON)。它也应该更快,因为从 L1 缓存检索的数据缓存更少(由于间接,需要更多的提取)。
确实,dans[j] += weights[k]
可以通过加载 m
进行矢量化dans
的项目和m
weights
的项目添加m
使用一条指令并存储 m
的项目项目返回 dans
。该解决方案高效且缓存友好。
dans[strides[i]] += weights[i]
无法在大多数主流硬件上进行有效矢量化。由于间接性,处理器需要从内存层次结构中执行昂贵的收集,然后进行求和,然后执行同样昂贵的分散存储。即使strides
将包含连续索引,这些指令通常比从内存加载连续数据 block 昂贵得多。此外,编译器经常无法对代码进行矢量化,或者只是发现在这种情况下不值得使用 SIMD 指令。因此,生成的代码可能是效率较低的标量代码。
实际上,在具有良好编译标志的现代处理器上,这两个代码之间的性能差异应该更大。我怀疑你在这里只在 x86 处理器上使用 SSE,因此理论上速度接近 2,因为可以连续计算 2 个 double float 。然而,使用 AVX/AVX-2 理论上会导致速度接近 4(因为可以连续计算 4 个数字)。最近的英特尔处理器甚至可以连续计算 8 个 double float 。请注意,计算简单精度 float 也可以实现理论上 2 倍的加速。这同样适用于其他架构,例如具有 NEON 和 SVE 指令集或 POWER 的 ARM。由于 future 的处理器可能会使用更宽的 SIMD 寄存器(因为它们的效率),因此编写 SIMD 友好的代码非常重要。
关于python - 预先计算数组的跨步访问模式会带来更差的性能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67787501/
如果不耐烦,请跳到下面的“问题”标题。 语境 我从事Unix(如)系统管理和基础结构开发的工作,但我认为程序员最好回答我的问题:o) 我想做的是学习如何使用iozone对文件系统(普通文件,托管卷,虚
我正在做一个项目,其中使用了Visual Studio代码进行调试。我需要“踏入”,“踏出”和“踏出”之间的明显区别。我看到了vs代码调试文档,但没有与此相关的详细信息。有人知道吗? 最佳答案 介入:
) 我更新了一个“锻炼对象”,使其具有最小和最大重复次数。 当我在 Playground 上对下界进行硬编码时,我一直在使用: let numberOfExercises = Int(arc4rand
我也尝试了2解决方案。但这没有用。 转到Visual Studio中的“工具”>“选项”菜单。 转到左 Pane 中的“调试”>“常规”菜单项。 在右 View 中,您将看到和选择Step over
这就是我实例化 ImageReader 的方式。 Size[] sizes = configs.getOutputSizes(ImageFormat.YUV_420_888); mIm
我是一名优秀的程序员,十分优秀!