gonum: mat: performance of calculating eigenvalues in gonum is slower than numpy

What are you trying to do?

I am comparing the performance of certain numpy operations against gonum for equivalency. In this specific benchmark I am assessing the performance of calculating the eigenvalues of a matrix to determine whether the matrix is positive definite.

What did you do?

I ran the following scripts in Python and Go respectively:

import numpy as np

def is_pos_def(x):
    return np.all(np.linalg.eigvals(x) > 0)

a = np.random.rand(1000, 1000)
print is_pos_def(a)
package main

import (
	"fmt"
	"math/rand"
	"gonum.org/v1/gonum/mat"
)

func randMat(d int) mat.Matrix {
	data := make([]float64, d*d)
	for i := range data {
		data[i] = rand.NormFloat64()
	}
	return mat.NewDense(d, d, data)
}

func isPosDef(x mat.Matrix) bool {
	var eig mat.Eigen
	eig.Factorize(x, false, false)
	allGreaterThanZero := true
	for _, v := range eig.Values(nil) {
		if real(v) <= 0.0 {
			allGreaterThanZero = false
			break
		}
	}
	return allGreaterThanZero
}

func main() {
	a := randMat(1000)
	fmt.Println(isPosDef(a))
}

What did you expect to happen?

I expected the Go script to run equally fast as the Python script

What actually happened?

The Go script runs about 5x slower than the Python script:

python demo.py  1.36s user 0.12s system 135% cpu 1.092 total
go run demo.go  6.04s user 0.26s system 129% cpu 4.855 total

The Go script runs about 16x slower when left and/or right are set to true when calculating the eigenvector factorization. Am I setting up the Go code incorrectly at all or is there something degrading performance in the library implementation?

What version of Go and Gonum are you using?

$ go version
go version go1.10 darwin/amd64
gonum$ git rev-parse HEAD
c75679ee1eff8582bca7583704825721ea1ff701

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Comments: 16 (8 by maintainers)

Most upvoted comments

In conjunction with the installation of the “gonum.org/v1/netlib/lapack/netlib” package (needs CGO and is built according to instruction at the link above), you need to change your imports to include

	"gonum.org/v1/gonum/lapack/lapack64"
	"gonum.org/v1/netlib/lapack/netlib"

and main to be

func main() {
	lapack64.Use(netlib.Implementation{})

	a := randMat(1000)
	fmt.Println(isPosDef(a))
}

Do you have some documentation on how I can switch the linear algebra backends?

https://github.com/gonum/netlib

Sounds like you’re hinting at swapping out Dlaqr5 for OpenBLAS.

Not only Dlaqr5, this will swap out the whole eigen solver, the entry routine is Dgeev.

I can fix my example later, but mainly want to get to the heart of the performance issue first.

If you need to check whether your matrix is positive definite, then you are most likely assessing performance of the wrong eigen solver.

Checking the pprof output from @sbinet , it seems that most time in Dlaqr5 is spent on lines doing just index calculations and accessing slices. The next step would be to look at the generated assembly.

Another source of performance penalty might come from the fact that we are row-major. The original Fortran code is column-major which fits better the algorithm that works with columns and so the memory access pattern is more cache friendly.