9

我们最近将 SonarQube 服务器升级到最新版本 (5.3) 并升级了所有插件。该服务器目前仅监视由 Visual Studio Team Services(之前是 Visual Studio Online)构建的 C# 项目的单个构建。

起初,一切都很好(除了一个长期存在的问题,我将在另一个问题中描述)。但过了一段时间,我们在尝试将数据推送到 SQ 时开始出现如下错误:

ERROR: Error during Sonar runner execution
org.sonar.runner.impl.RunnerException: Unable to execute Sonar
    at org.sonar.runner.impl.BatchLauncher$1.delegateExecution(BatchLauncher.java:91)
    at org.sonar.runner.impl.BatchLauncher$1.run(BatchLauncher.java:75)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.sonar.runner.impl.BatchLauncher.doExecute(BatchLauncher.java:69)
    at org.sonar.runner.impl.BatchLauncher.execute(BatchLauncher.java:50)
    at org.sonar.runner.api.EmbeddedRunner.doExecute(EmbeddedRunner.java:102)
    at org.sonar.runner.api.Runner.execute(Runner.java:100)
    at org.sonar.runner.Main.executeTask(Main.java:70)
    at org.sonar.runner.Main.execute(Main.java:59)
    at org.sonar.runner.Main.main(Main.java:53)
Caused by: java.lang.IllegalStateException: Can't create measure for line 47 for file '[PATH]/[FILENAME].cs' with 45 lines
    at org.sonar.batch.sensor.coverage.CoverageExclusions.validateMaxLine(CoverageExclusions.java:158)
    at org.sonar.batch.sensor.coverage.CoverageExclusions.validate(CoverageExclusions.java:129)
    at org.sonar.batch.deprecated.DeprecatedSensorContext.saveMeasure(DeprecatedSensorContext.java:204)
    at org.sonar.plugins.dotnet.tests.CoverageReportImportSensor.analyze(CoverageReportImportSensor.java:78)
    at org.sonar.plugins.dotnet.tests.CoverageReportImportSensor.analyse(CoverageReportImportSensor.java:59)
    at org.sonar.batch.phases.SensorsExecutor.executeSensor(SensorsExecutor.java:58)
    at org.sonar.batch.phases.SensorsExecutor.execute(SensorsExecutor.java:50)
    at org.sonar.batch.phases.PhaseExecutor.execute(PhaseExecutor.java:98)
    at org.sonar.batch.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:185)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:132)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:117)
    at org.sonar.batch.scan.ProjectScanContainer.scan(ProjectScanContainer.java:243)
    at org.sonar.batch.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:238)
    at org.sonar.batch.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:236)
    at org.sonar.batch.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:228)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:132)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:117)
    at org.sonar.batch.task.ScanTask.execute(ScanTask.java:55)
    at org.sonar.batch.task.TaskContainer.doAfterStart(TaskContainer.java:86)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:132)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:117)
    at org.sonar.batch.bootstrap.GlobalContainer.executeTask(GlobalContainer.java:122)
    at org.sonar.batch.bootstrapper.Batch.executeTask(Batch.java:119)
    at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:79)
    at org.sonar.runner.batch.IsolatedLauncher.execute(IsolatedLauncher.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.sonar.runner.impl.BatchLauncher$1.delegateExecution(BatchLauncher.java:87)
    ... 9 more

我们找到了一篇文章 ( https://groups.google.com/forum/#!topic/sonarqube/Xju6ichZe_k ),其中描述了一种解决方案,涉及更改其中一个插件 (sonar-dotnet-tests-library) 的代码并替换在服务器上构建文件。作为 .NET 负责人,我们不介意不必修改其他人的 Java 库来使其工作;)

这是问题的解决方案,还是有其他可能导致此问题的原因?目前它阻止我们将数据推送到 SonarQube,这是一种耻辱......


注意:这个答案不是关于如何从循环顺序中获得最佳性能或如何并行化它,因为我认为由于多种原因它不是最理想的。我将尝试就如何改进订单(并将其并行化)提供一些建议。

循环顺序

OpenMP 通常用于在多个 CPU 上分配工作。因此,您希望最大化每个线程的工作量,同时最小化所需的数据和信息传输量。

  1. 您希望并行执行最外层循环而不是第二个循环。因此,您需要将其中一个r_matrix索引作为外部循环索引,以避免在写入结果矩阵时出现竞争条件。

  2. 下一件事是您要按内存存储顺序遍历矩阵(将变化更快的索引作为第二个而不是第一个下标索引)。

您可以通过以下循环/索引顺序实现两者:

for i = 0 to a_rows
  for k = 0 to a_cols
    for j = 0 to b_cols
      r[i][j] = a[i][k]*b[k][j]

在哪里

  • j变化快于i或变化快k于。ki
  • i是结果矩阵下标,i循环可以并行运行

以这种方式重新排列你multiply_matrices_KIJ已经提供了相当多的性能提升。

我做了一些简短的测试,我用来比较时间的代码是:

template<class T>
void mm_kij(T const * const matrix_a, std::size_t const a_rows, 
  std::size_t const a_cols, T const * const matrix_b, std::size_t const b_rows, 
  std::size_t const b_cols, T * const matrix_r)
{
  for (std::size_t k = 0; k < a_cols; k++)
  {
    for (std::size_t i = 0; i < a_rows; i++)
    {
      for (std::size_t j = 0; j < b_cols; j++)
      {
        matrix_r[i*b_cols + j] += 
          matrix_a[i*a_cols + k] * matrix_b[k*b_cols + j];
      }
    }
  }
}

模仿你的multiply_matrices_KIJ()功能与

template<class T>
void mm_opt(T const * const a_matrix, std::size_t const a_rows, 
  std::size_t const a_cols, T const * const b_matrix, std::size_t const b_rows, 
  std::size_t const b_cols, T * const r_matrix)
{
  for (std::size_t i = 0; i < a_rows; ++i)
  { 
    T * const r_row_p = r_matrix + i*b_cols;
    for (std::size_t k = 0; k < a_cols; ++k)
    { 
      auto const a_val = a_matrix[i*a_cols + k];
      T const * const b_row_p = b_matrix + k * b_cols;
      for (std::size_t j = 0; j < b_cols; ++j)
      { 
        r_row_p[j] += a_val * b_row_p[j];
      }
    }
  }
}

执行上述命令。

Intel i5-2500k 上两个2048x2048矩阵相乘的时间消耗

  • mm_kij(): 6.16706s。

  • mm_opt(): 2.6567 秒。

给定的顺序还允许外循环并行化,而不会在写入结果矩阵时引入任何竞争条件:

template<class T>
void mm_opt_par(T const * const a_matrix, std::size_t const a_rows, 
  std::size_t const a_cols, T const * const b_matrix, std::size_t const b_rows, 
  std::size_t const b_cols, T * const r_matrix)
{
#if defined(_OPENMP)
  #pragma omp parallel
  {
    auto ar = static_cast<std::ptrdiff_t>(a_rows);
    #pragma omp for schedule(static) nowait
    for (std::ptrdiff_t i = 0; i < ar; ++i)
#else
    for (std::size_t i = 0; i < a_rows; ++i)
#endif
    {
      T * const r_row_p = r_matrix + i*b_cols;
      for (std::size_t k = 0; k < b_rows; ++k)
      {
        auto const a_val = a_matrix[i*a_cols + k];
        T const * const b_row_p = b_matrix + k * b_cols;
        for (std::size_t j = 0; j < b_cols; ++j)
        {
          r_row_p[j] += a_val * b_row_p[j];
        }
      }
    }
#if defined(_OPENMP)
  }
#endif
}

每个线程写入单个结果行的位置

Intel i5-2500k(4 个 OMP 线程)上两个2048x2048矩阵相乘的时间消耗

  • mm_kij(): 6.16706s。

  • mm_opt(): 2.6567 秒。

  • mm_opt_par(): 0.968325s。

不是完美的缩放,但作为一个比串行代码更快的开始。

4

2 回答 2

3

There seems to be a problem in the Code Coverage tooling that ships with Visual Studio 2015. The problem should be fixed with Visual Studio 2015 Update 3.

The hosted agent should be updated with Update 3 soon after it is released as well, the TFS build team are quite fast on that. For a list of software available on the hosted agent see here.

Workarounds until then:

  1. don't generate a code coverage file, for example by disabling the option in the VS Test build task
  2. some users reported the problem does not occur with VS 2013, so you might want to build and/or test with it instead of using VS 2015
于 2016-06-09T10:57:07.690 回答
1

对我来说,这是因为之前构建的 OpenCover 报告被遗留在 Jenkins 上。

一旦我确保它们都被删除,问题就消失了,我假设有多个报告意味着它试图多次处理文件。

于 2016-06-08T06:45:08.817 回答