【openvino系列教程(三)】浮点数例外——broadwell架构无法获取cpu cores

一、问题描述

A 机器上可以使用openvino
B 无法使用openvino

二、原因

broadwell架构(支持avx-512)的cpu机器,系统配置文件/proc/cpuinfo中获取不到cpu cores字段。

  • 参考源码:Openvino的源码中:https://github.com/opencv/dldt/tree/2018
    inference-engine/src/mkldnn_plugin/mkldnn/os/lin/lin_omp_manager.cpp

1. A能获取到cpu cores,而B获取不到

getTotalNumberOfCpuCores()函数最终调用Collection::parseValue()函数解析linux系统文件/proc/cpuinfo中的processorphysical idcpu cores字段。而B的机器上可能因为是broadwell架构,/proc/cpuinfo文件中不包含cpu cores字段,因此返回默认值0,导致在 getTotalNumberOfCpuCores()函数调用unsigned coreId = processorId % totalNumberOfCpuCores;这条语句时,由于除数为0,因此出现浮点数例外,即SIGFPE - Floating point error signal的错误。

void Collection::parseValue(const char *fieldName, const char *valueString) {
    if (!currentProcessor) {
        appendNewProcessor();
    }

    if (beginsWith(fieldName, "processor")) {
        currentProcessor->processor = parseInteger(valueString);
    }

    if (beginsWith(fieldName, "physical id")) {
        currentProcessor->physicalId = parseInteger(valueString);
    }

    if (beginsWith(fieldName, "cpu cores")) {
        currentProcessor->cpuCores = parseInteger(valueString);
    }
}

【openvino系列教程(三)】浮点数例外——broadwell架构无法获取cpu cores_第1张图片
【openvino系列教程(三)】浮点数例外——broadwell架构无法获取cpu cores_第2张图片

三、调用逻辑

     程序实际是如何根据“/proc/cpuinfo”文件中的processorphysical idcpu cores字段。,获取到最终的numberOfProcessorstotalNumberOfCpuCorestotalNumberOfSockets呢?
我们以正常机器A为例,看看这几个机器的processorphysical idcpu cores字段字段分别是什么?

3. 1 获取 processor

[root@A /opt/intel/openvino]# cat /proc/cpuinfo |grep "processor"
processor       : 0
processor       : 1
processor       : 2
processor       : 3
processor       : 4
processor       : 5
processor       : 6
processor       : 7
processor       : 8
processor       : 9
processor       : 10
processor       : 11

见注释:

unsigned Collection::getNumberOfProcessors() {
    return processors.size(); // 3. 最终容器processors的size就是A这台机器的processor个数,numberOfProcessors = 12。
}

void Collection::parseCpuInfoLine(const char *cpuInfoLine) {
    int delimiterPosition = strcspn(cpuInfoLine, ":");

    if (cpuInfoLine[delimiterPosition] == '\0') { // 1. 碰到空行时,将currentProcessor设置为NULL
        currentProcessor = NULL;
    } else {
        parseValue(cpuInfoLine, &cpuInfoLine[delimiterPosition + 2]);
    }
}

void Collection::parseValue(const char *fieldName, const char *valueString) {
	 // 2. 调用currentProcessor->physicalId = parseInteger(valueString);解析physical id时,
	 // currentProcessor由于先碰到空行,被设置为NULL。因此会给新增一个元素到容器processors中。
    if (!currentProcessor) {
        appendNewProcessor(); 
    }

    if (beginsWith(fieldName, "processor")) {
        currentProcessor->processor = parseInteger(valueString);
    }

    if (beginsWith(fieldName, "physical id")) {
        currentProcessor->physicalId = parseInteger(valueString);
    }

    if (beginsWith(fieldName, "cpu cores")) {
        currentProcessor->cpuCores = parseInteger(valueString);
    }
}

void Collection::appendNewProcessor() {
    processors.push_back(Processor());
    currentProcessor = &processors.back();
}

3. 2 获取physical idcpu cores

[root@A /opt/intel/openvino]# cat /proc/cpuinfo |grep "physical id"
physical id     : 0
physical id     : 0
physical id     : 0
physical id     : 1
physical id     : 1
physical id     : 1
physical id     : 2
physical id     : 2
physical id     : 2
physical id     : 3
physical id     : 3
physical id     : 3
[root@A /opt/intel/openvino]# cat /proc/cpuinfo |grep "cpu cores"
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3
cpu cores       : 3

见注释:

void Collection::collectBasicCpuInformation() {
    // 0. set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。Set中元素的值不能直接被改变。
    std::set<unsigned> uniquePhysicalId; 
    std::vector<Processor>::iterator processor = processors.begin();
     // 1. 根据processors的size=12对每一个processor->physicalId进行查询和判断。
    for (; processor != processors.end(); processor++) {
        // 2. 将12个processor中的physicalId都插入到容器std::set中
        uniquePhysicalId.insert(processor->physicalId); 
        // 3. uniquePhysicalId.size()返回时不重复的元素的个数,
        // 即将12个physical id = [0 0 0 1 1 1 2 2 2 3 3 3]不重复的元素个数求解出来。
        updateCpuInformation(*processor, uniquePhysicalId.size()); 
    }
}

void Collection::updateCpuInformation(const Processor &processor,
                                      unsigned numberOfUniquePhysicalId) {
    // 4. 例如,当processor取到第0个时,totalNumberOfSockets = numberOfUniquePhysicalId = 1,
    // 当processor取到第1~2个时,totalNumberOfSockets == numberOfUniquePhysicalId,
    // 因此将第1~2个processor的 physical id 和 对应的cpu cores 跳过,避免重复加和到totalNumberOfCpuCores上。
    // 直到processor取到第3个,numberOfUniquePhysicalId = uniquePhysicalId.size()更新为2,重复以上操作。
    if (totalNumberOfSockets == numberOfUniquePhysicalId) {
        return;
    }
    // 5. 因此,totalNumberOfSockets 记录的是实际的物理cpu的个数,
    //  totalNumberOfCpuCores记录的是实际的cpu核数 =  物理cpu的个数 * [**cpu core**]
    totalNumberOfSockets = numberOfUniquePhysicalId;
    totalNumberOfCpuCores += processor.cpuCores; // 实际只加和了4次3.
}

3.3 计算totalNumberOfCpuCores

  • numberOfProcessors:由于processors.size()=12,表示一共有12个processor。
  • cpu cores = 3 :表示每个processor对应的cpu cores = 3 记录的是每个processor所对应的那个物理CPUCore的个数。
  • totalNumberOfSockets:表示12个physical id = [0 0 0 1 1 1 2 2 2 3 3 3]不重复的元素个数,即实际的物理cpu的个数,= 4。
  • totalNumberOfCpuCores:表示实际的cpu核数 = 物理cpu的个数 * [cpu core] = 4 * 3。
    这里numberOfProcessors = totalNumberOfCpuCores

四、解决办法

第一章分析了,是因为B的机器上可能因为是broadwell架构,其/proc/cpuinfo文件中不包含cpu cores字段,因此,虽然numberOfProcessors=12,但totalNumberOfCpuCores=0,因此可以对于broadwell架构机器,判断,如果totalNumberOfCpuCores==0,则将totalNumberOfCpuCores 设置为 numberOfProcessors

你可能感兴趣的:(openvino)