当我添加 if 条件时,通过 ctypes 在 python 中使用的 C 函数变慢
C functions used in python via ctypes slows down when I add if condition
我尝试实现一个循环遍历数据集的 C 函数(随机生成 100 万个特征向量),并在每次迭代时使用给定的特征向量处理点积运算。它非常快地完成循环(在 ~0.05 毫秒内)。
c代码:
void dotProduct(double *parameters, double *feature, double *dataset)
{
const int FEATURE_COUNT = parameters[0];
const int DATASET_COUNT = parameters[1];
const double THRESHOLD = parameters[2];
double dotProductResult;
for (int i = 0; i < DATASET_COUNT; i++)
{
dotProductResult = 0.0;
for (int j = 0; j < FEATURE_COUNT; j++)
{
dotProductResult += dataset[i*FEATURE_COUNT+j] * feature[j];
}
// if (dotProductResult > THRESHOLD)
// {
// parameters[3] = i;
// }
}
}
python代码:
ND_POINTER_PARAMETERS = np.ctypeslib.ndpointer(dtype=np.float64,
ndim=1,
flags="C")
ND_POINTER_FEATURE = np.ctypeslib.ndpointer(dtype=np.float64,
ndim=1,
flags="C")
ND_POINTER_DATASET = np.ctypeslib.ndpointer(dtype=np.float64,
ndim=2,
flags="C")
dotProductLib.dotProduct.argtypes = [ND_POINTER_PARAMETERS, ND_POINTER_FEATURE, ND_POINTER_DATASET]
feature = np.random.rand(512,)
dataset = np.random.rand(1000000,512)
parameters = np.array([len(feature), len(dataset), 200, -1], dtype=np.float64)
t = time()
dotProductLib.dotProduct(parameters, feature, dataset)
print("---time taken C: ",1000*(time()-t), "ms")
然而,当我添加一个 if
条件来检查点积的结果是否高于给定的阈值并将向量的索引分配给给定的地址时,速度会急剧下降(~500ms ) 即使它永远不会进入 if 条件。
此外,当我添加 std::cout
而不是变量赋值时,也会发生同样的事情。
if (dotProductResult > THRESHOLD)
{
std::cout << dotProductResult << "\n";
}
此外,如果我添加一个else
条件并在if
中荒谬地写相同的语句或者如果我分配任何数字,它不会降低速度。
if (dotProductResult > THRESHOLD)
{
parameters[3] = i;
}
else
{
parameters[3] = i;
}
或
if (dotProductResult > THRESHOLD)
{
parameters[3] = 123;
}
我使用 -O3
标志。
我做错了什么?有什么建议可以快速进行这样的操作吗?
提前致谢
您的原始代码之所以快,是因为它实际上并没有做任何事情!编译器认识到您实际上并没有使用 dotProductResult
,因此该函数得到 optimized down to literally nothing。当您取消注释条件时,现在使用 dotProductResult
的值,因此它必须实际执行计算点积的工作。如果您“添加一个 else
条件并在 if
中编写相同的语句”,那么它又很快了,因为编译器意识到这等同于此:
void dotProduct(double *parameters, double *feature, double *dataset)
{
const int DATASET_COUNT = parameters[1];
if (DATASET_COUNT > 0)
{
parameters[3] = DATASET_COUNT - 1;
}
}
所以它可以再次停止工作。
我尝试实现一个循环遍历数据集的 C 函数(随机生成 100 万个特征向量),并在每次迭代时使用给定的特征向量处理点积运算。它非常快地完成循环(在 ~0.05 毫秒内)。
c代码:
void dotProduct(double *parameters, double *feature, double *dataset)
{
const int FEATURE_COUNT = parameters[0];
const int DATASET_COUNT = parameters[1];
const double THRESHOLD = parameters[2];
double dotProductResult;
for (int i = 0; i < DATASET_COUNT; i++)
{
dotProductResult = 0.0;
for (int j = 0; j < FEATURE_COUNT; j++)
{
dotProductResult += dataset[i*FEATURE_COUNT+j] * feature[j];
}
// if (dotProductResult > THRESHOLD)
// {
// parameters[3] = i;
// }
}
}
python代码:
ND_POINTER_PARAMETERS = np.ctypeslib.ndpointer(dtype=np.float64,
ndim=1,
flags="C")
ND_POINTER_FEATURE = np.ctypeslib.ndpointer(dtype=np.float64,
ndim=1,
flags="C")
ND_POINTER_DATASET = np.ctypeslib.ndpointer(dtype=np.float64,
ndim=2,
flags="C")
dotProductLib.dotProduct.argtypes = [ND_POINTER_PARAMETERS, ND_POINTER_FEATURE, ND_POINTER_DATASET]
feature = np.random.rand(512,)
dataset = np.random.rand(1000000,512)
parameters = np.array([len(feature), len(dataset), 200, -1], dtype=np.float64)
t = time()
dotProductLib.dotProduct(parameters, feature, dataset)
print("---time taken C: ",1000*(time()-t), "ms")
然而,当我添加一个 if
条件来检查点积的结果是否高于给定的阈值并将向量的索引分配给给定的地址时,速度会急剧下降(~500ms ) 即使它永远不会进入 if 条件。
此外,当我添加 std::cout
而不是变量赋值时,也会发生同样的事情。
if (dotProductResult > THRESHOLD)
{
std::cout << dotProductResult << "\n";
}
此外,如果我添加一个else
条件并在if
中荒谬地写相同的语句或者如果我分配任何数字,它不会降低速度。
if (dotProductResult > THRESHOLD)
{
parameters[3] = i;
}
else
{
parameters[3] = i;
}
或
if (dotProductResult > THRESHOLD)
{
parameters[3] = 123;
}
我使用 -O3
标志。
我做错了什么?有什么建议可以快速进行这样的操作吗?
提前致谢
您的原始代码之所以快,是因为它实际上并没有做任何事情!编译器认识到您实际上并没有使用 dotProductResult
,因此该函数得到 optimized down to literally nothing。当您取消注释条件时,现在使用 dotProductResult
的值,因此它必须实际执行计算点积的工作。如果您“添加一个 else
条件并在 if
中编写相同的语句”,那么它又很快了,因为编译器意识到这等同于此:
void dotProduct(double *parameters, double *feature, double *dataset)
{
const int DATASET_COUNT = parameters[1];
if (DATASET_COUNT > 0)
{
parameters[3] = DATASET_COUNT - 1;
}
}
所以它可以再次停止工作。