PVS-Studio对Abbyy NeoML代码的质量印象深刻

image1.png


最近,ABBYY发布了其NeoML框架的源代码。我们提供了使用PVS-Studio检查该库的信息。从分析的角度来看,这是一个有趣的项目,因此我们没有将其推迟到后炉。由于该项目的质量很高,所以您不会花很多时间阅读本文:)。



NeoML 源代码可以在GitHub找到该框架是跨平台的,旨在实现机器学习模型。ABBYY开发人员使用它来解决计算机视觉问题,自然语言处理(包括图像处理,文档分析等)。当前,支持C ++,Java,Objective-C等编程语言,并且Python应尽快添加到此列表中。编写框架本身的主要语言是C ++。



运行分析



在此框架上运行分析非常简单。在CMake中生成Visual Studio项目后,我在Visual Studio中为Solution中我们感兴趣的项目(不包括第三方库)启动了PVS-Studio分析。除了NeoML本身之外,该解决方案还包括来自ABBYY的NeoOnnx和NeoMathEngine之类的库。我也将它们包括在启动分析的项目列表中。



分析结果



当然,我真的很想找到一些可怕的错误,但是……代码证明是很干净的,仅收到警告,没有任何警告。开发可能已经使用了静态分析。许多警告是代码相似部分的相同诊断的触发器。



例如,在这个项目中,从构造函数调用虚拟方法非常普遍。通常,这是一种危险的方法。诊断V1053对以下情况作出响应在构造函数/析构函数中调用'foo'虚拟函数可能会在运行时导致意外结果。总共发出了10条此类警告。在斯科特·迈尔斯(Scott Myers)的这篇文章中,详细了解为什么这是一种危险的做法以及它会导致什么问题永远不要在构造或破坏过程中调用虚拟函数。但是,开发人员似乎理解他们在做什么,并且没有错误。“微优化”部分



还提供了11个V803中级诊断警告。此诊断建议用前缀增量替换后缀增量,如果不使用先前的迭代器值,则在后缀增加的情况下,会创建一个额外的临时对象。当然,这不是错误,只是一个很小的细节。如果这样的诊断没有意思,则可以使用分析器将其关闭。 “优化”,默认情况下处于关闭状态。



实际上,我认为您了解,因为我们已经对文章中的迭代器增量这样的琐事进行了分析,所以这意味着一切都很好,而我们只是不知道要弥补什么。



很多时候,某些诊断对用户可能不适用或不感兴趣,因此最好不要吃仙人掌,而要花一些时间来设置分析仪。你可以阅读更多关于要采取的步骤,以立即得到更接近我们的文章中最有趣的分析仪响应“ 如何快速看到有趣的警告,在PVS-Studio的分析生成C和C ++代码吗?



在从节中的触发器“微优化也有有趣的V802诊断警告,建议按类型大小的降序排列结构字段,这样可以减小结构的大小。



V802在64位平台上,可以根据字段大小的降序重新排列,从而将结构大小从24字节减少到16个字节。HierarchicalClustering.h 31



struct CParam {
  TDistanceFunc DistanceType; 
  double MaxClustersDistance;
  int MinClustersCount; 
};


通过 简单地将MaxClustersDistance字段double类型DistanceType枚举数交换可以将结构大小从24个字节减少到16个字节。



struct CParam {
  double MaxClustersDistance;
  int MinClustersCount; 
  TDistanceFunc DistanceType; 
};


TDistanceFunc是一个枚举,因此其大小等于int或更小,因此应将其移到结构的末尾。



同样,这不是一个错误,但是如果用户对进行微优化感兴趣,或者从原则上讲对于项目来说很重要,则此类分析器触发器可让您快速找到至少用于主要重构的位置。



通常,所有代码均准确且清晰地编写,但是V807诊断程序指出了几个可以使其更加优化和易读的地方。让我给你一个最有说明意义的例子:



V807性能下降。考虑创建引用以避免重复使用相同的表达式。 469第469章



image3.png


可以将对curLevelStatistics [i]-> ThreadStatistics [j]的调用替换为对单独变量的调用。在此链中,没有任何复杂方法的调用,因此这里可能没有任何特殊的优化,但是我认为代码将更易于阅读和紧凑。此外,在将来获得此代码的支持后,很明显可以准确地处理这些索引,并且这里没有错误。为了清楚起见,我将为代码提供变量的替换:



auto threadStatistics = curLevelStatistics[i]->ThreadStatistics[j];

if(threadStatistics.FeatureIndex != NotFound ) {
  if(   threadStatistics.Criterion > criterion
     || ( .... ))
  {
    criterion = threadStatistics.Criterion;
    curLevelStatistics[i]->FeatureIndex    = threadStatistics.FeatureIndex;
    curLevelStatistics[i]->Threshold       = threadStatistics.Threshold;
    curLevelStatistics[i]->LeftStatistics  = threadStatistics.LeftStatistics;
    curLevelStatistics[i]->RightStatistics = threadStatistics.RightStatistics;
  }
}


结论



如您所见,从静态分析的角度来看,该框架的代码库非常干净。



应该理解的是,在一个积极开发的项目上启动分析的能力微弱地反映了对静态分析的需求,因为已经以其他方式检测到许多错误,尤其是如果它们是关键错误,但是这要花费更多的时间和资源。在文章“ 由于未使用静态代码分析而未发现错误对此进行了更详细的分析



但是即使考虑到这一事实,NeoML也很少发出警告,并且我想表达对这个项目中代码质量的尊重,无论开发人员是否使用静态分析。





如果您想与讲英语的读者分享这篇文章,请使用翻译链接:Victoria Khanieva。ABBYY NeoML的代码质量给PVS-Studio留下了深刻的印象



All Articles