You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python+C稳定API实现K-means时大数据集触发段错误问题排查

问题分析与修复方案

核心问题定位

1. C代码中的致命逻辑错误

在convertCArrayToDoubleList函数中,检查current_center创建是否成功的判断条件完全错误:

// 错误代码
if(!K_centroid_list){
    Py_DECREF(K_centroid_list);
    return NULL;
}

这里应该判断current_center是否为NULL,而非重复检查K_centroid_list。该错误会导致current_center创建失败时,程序既不释放已分配资源,还会继续执行后续代码,直接触发段错误或内存泄漏。

2. 全局变量滥用导致越界访问

函数直接使用全局变量K和d,若这两个值在函数调用过程中被意外修改(比如多次调用K-means时参数变化),会引发数组索引越界,触发段错误。正确做法是将K和d作为参数传入函数。

3. 错误处理不完整引发内存泄漏

当创建PyFloat对象失败时,仅释放当前的K_centroid_list和current_center,但之前已成功创建并添加到K_centroid_list中的子列表未被遍历释放,造成内存泄漏。

修复后的C代码

PyObject* convertCArrayToDoubleList(double* arr, int K, int d){
    int i, j;
    PyObject* K_centroid_list = PyList_New(K);
    if(!K_centroid_list)
        return NULL;
    
    for(i=0;i<K;++i){
        PyObject* current_center = PyList_New(d);
        if(!current_center){
            // 清理已创建的子列表
            for(int k=0; k<i; k++){
                Py_DECREF(PyList_GetItem(K_centroid_list, k));
            }
            Py_DECREF(K_centroid_list);
            return NULL;
        }
        for(j=0;j<d;++j){
            PyObject* num = PyFloat_FromDouble(arr[i*d+j]);
            if(!num){
                // 清理当前子列表的已创建元素
                for(int k=0; k<j; k++){
                    Py_DECREF(PyList_GetItem(current_center, k));
                }
                Py_DECREF(current_center);
                // 清理已创建的子列表
                for(int k=0; k<i; k++){
                    Py_DECREF(PyList_GetItem(K_centroid_list, k));
                }
                Py_DECREF(K_centroid_list);
                return NULL;
            }
            PyList_SET_ITEM(current_center,j,num);
        }
        PyList_SET_ITEM(K_centroid_list,i,current_center);
    }
    return K_centroid_list;
}

内存泄漏排查补充建议

  • valgrind详细追踪:执行命令 valgrind --leak-check=full --show-leak-kinds=all python your_script.py,查看具体泄漏栈信息,重点检查C代码中malloc/calloc分配的数组是否在函数结束前被正确free。
  • 迭代部分内存检查:确保K-means迭代过程中动态分配的临时数组(如距离计算缓冲区、簇分配数组)在每次迭代结束或函数退出时被释放。
  • Python引用计数校验:所有通过Py_*_New创建的对象,除了用PyList_SET_ITEM添加到列表中的(该函数自动接管引用),其他未被容器持有的对象必须用Py_DECREF释放。

Python代码优化建议

将numpy数组转成Python list再传入C会额外复制数据,大数据集下效率极低。建议直接通过PyArray_DATA获取numpy数组的指针传递给C代码,避免数据复制,减少内存占用。

内容的提问来源于stack exchange,提问作者Roy Dahan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 04:52:10