include(joinpath(LOOPVECBENCHDIR, "looptests.jl"))
include(joinpath(LOOPVECBENCHDIR, "loadsharedlibs.jl"))

using BenchmarkTools, SharedArrays
struct SizedResults{V<:AbstractVector} <: AbstractMatrix{String}
  results::Matrix{Float64}
  sizes::V
end
function Base.size(sr::SizedResults)
  M, N = size(sr.results)
  N, M + 1
end
struct BenchmarkResult{V}
  tests::Vector{String}
  sizedresults::SizedResults{V}
end
function BenchmarkResult(results, tests, sizes)
  ntests = length(tests)
  nsizes = length(sizes)
  BenchmarkResult(append!(["Size"], tests), SizedResults(results, sizes))
end
function Base.getindex(br::SizedResults, row, col)
  col == 1 ? string(br.sizes[row]) : string(br.results[col-1, row])
end
Base.setindex!(br::BenchmarkResult, v, i...) = br.sizedresults.results[i...] = v
function Base.vcat(br1::BenchmarkResult, br2::BenchmarkResult)
  BenchmarkResult(
    br1.tests,
    SizedResults(
      hcat(br1.sizedresults.results, br2.sizedresults.results),
      vcat(br1.sizedresults.sizes, br2.sizedresults.sizes)
    )
  )
end

tothreetuple(i::Int) = (i, i, i)
tothreetuple(i::NTuple{3,Int}) = i
function matmul_bench!(br, C, A, B, i)
  M, N = size(C)
  K = size(B, 1)
  n_gflop = M * K * N * 2e-9
  Cblas = A * B
  br[1, i] = n_gflop / @belapsed gemmavx!($C, $A, $B)
  @assert C ≈ Cblas "LoopVec gemm wrong?"
  fill!(C, NaN)
  br[2, i] = n_gflop / @belapsed jgemm!($C, $A, $B)
  @assert C ≈ Cblas "Julia gemm wrong?"
  fill!(C, NaN)
  br[3, i] = n_gflop / @belapsed cgemm!($C, $A, $B)
  @assert C ≈ Cblas "Clang gemm wrong?"
  fill!(C, NaN)
  br[4, i] = n_gflop / @belapsed fgemm!($C, $A, $B)
  @assert C ≈ Cblas "Fort gemm wrong?"
  fill!(C, NaN)
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icgemm!($C, $A, $B)
    @assert C ≈ Cblas "icc gemm wrong?"
    fill!(C, NaN)
    br[6, i] = n_gflop / @belapsed ifgemm!($C, $A, $B)
    @assert C ≈ Cblas "ifort gemm wrong?"
    fill!(C, NaN)
  end
  br[5+2INTEL_BENCH, i] = n_gflop / @belapsed egemm!($C, $A, $B)
  @assert C ≈ Cblas "eigen gemm wrong?"
  fill!(C, NaN)
  br[6+2INTEL_BENCH, i] = n_gflop / @belapsed iegemm!($C, $A, $B)
  @assert C ≈ Cblas "i-eigen gemm wrong?"
  fill!(C, NaN)

  br[7+2INTEL_BENCH, i] = n_gflop / @belapsed fgemm_builtin!($C, $A, $B)
  @assert C ≈ Cblas "Fort builtin gemm wrong?"
  fill!(C, NaN)
  if INTEL_BENCH
    br[8+2INTEL_BENCH, i] = n_gflop / @belapsed ifgemm_builtin!($C, $A, $B)
    @assert C ≈ Cblas "ifort builtin gemm wrong?"
    fill!(C, NaN)
  end
  br[8+3INTEL_BENCH, i] = n_gflop / @belapsed gemmopenblas!($C, $A, $B)
  @assert C ≈ Cblas "OpenBLAS gemm wrong?"
  if MKL_BENCH
    br[9+3INTEL_BENCH, i] = n_gflop / @belapsed gemmmkl!($C, $A, $B)
    @assert C ≈ Cblas "MKL gemm wrong?"
  end
  # br[12,i] = n_gflop / @belapsed gemmavx!($C, $A, $B)
end
function A_mul_B_bench!(br, s, i)
  M, K, N = tothreetuple(s)
  C = Matrix{Float64}(undef, M, N)
  A = rand(M, K)
  B = rand(K, N)
  matmul_bench!(br, C, A, B, i)
end
function A_mul_B_bench!(br, s, i)
  M, K, N = tothreetuple(s)
  C = Matrix{Float64}(undef, M, N)
  A = rand(M, K)
  B = rand(K, N)
  matmul_bench!(br, C, A, B, i)
end
function A_mul_Bt_bench!(br, s, i)
  M, K, N = tothreetuple(s)
  C = Matrix{Float64}(undef, M, N)
  A = rand(M, K)
  B = rand(N, K)'
  matmul_bench!(br, C, A, B, i)
end
function At_mul_B_bench!(br, s, i)
  M, K, N = tothreetuple(s)
  C = Matrix{Float64}(undef, M, N)
  A = rand(K, M)'
  B = rand(K, N)
  matmul_bench!(br, C, A, B, i)
end
function At_mul_Bt_bench!(br, s, i)
  M, K, N = tothreetuple(s)
  C = Matrix{Float64}(undef, M, N)
  A = rand(K, M)'
  B = rand(N, K)'
  matmul_bench!(br, C, A, B, i)
end

function dot_bench!(br, s, i)
  a = rand(s)
  b = rand(s)
  dotblas = dot(a, b)
  n_gflop = s * 2e-9
  br[1, i] = n_gflop / @belapsed jdotavx($a, $b)
  @assert jdotavx(a, b) ≈ dotblas "LoopVec dot wrong?"
  br[2, i] = n_gflop / @belapsed jdot($a, $b)
  @assert jdot(a, b) ≈ dotblas "Julia dot wrong?"
  br[3, i] = n_gflop / @belapsed cdot($a, $b)
  @assert cdot(a, b) ≈ dotblas "Clang dot wrong?"
  br[4, i] = n_gflop / @belapsed fdot($a, $b)
  @assert fdot(a, b) ≈ dotblas "Fort dot wrong?"
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icdot($a, $b)
    @assert icdot(a, b) ≈ dotblas "icc dot wrong?"
    br[6, i] = n_gflop / @belapsed ifdot($a, $b)
    @assert ifdot(a, b) ≈ dotblas "ifort dot wrong?"
  end
  br[5+2INTEL_BENCH, i] = n_gflop / @belapsed edot($a, $b)
  @assert edot(a, b) ≈ dotblas "eigen dot wrong?"
  br[6+2INTEL_BENCH, i] = n_gflop / @belapsed iedot($a, $b)
  @assert iedot(a, b) ≈ dotblas "i-eigen dot wrong?"
  # br[9,i] = n_gflop / @belapsed dot($a, $b)
end
function selfdot_bench!(br, s, i)
  a = rand(s)
  b = rand(s)
  dotblas = dot(a, a)
  n_gflop = s * 2e-9
  br[1, i] = n_gflop / @belapsed jselfdotavx($a)
  @assert jselfdotavx(a) ≈ dotblas "LoopVec dot wrong?"
  br[2, i] = n_gflop / @belapsed jselfdot($a)
  @assert jselfdot(a) ≈ dotblas "Julia dot wrong?"
  br[3, i] = n_gflop / @belapsed cselfdot($a)
  @assert cselfdot(a) ≈ dotblas "Clang dot wrong?"
  br[4, i] = n_gflop / @belapsed fselfdot($a)
  @assert fselfdot(a) ≈ dotblas "Fort dot wrong?"
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icselfdot($a)
    @assert cselfdot(a) ≈ dotblas "icc dot wrong?"
    br[6, i] = n_gflop / @belapsed ifselfdot($a)
    @assert fselfdot(a) ≈ dotblas "ifort dot wrong?"
  end
  br[5+2INTEL_BENCH, i] = n_gflop / @belapsed eselfdot($a)
  @assert eselfdot(a) ≈ dotblas "eigen dot wrong?"
  br[6+2INTEL_BENCH, i] = n_gflop / @belapsed ieselfdot($a)
  @assert ieselfdot(a) ≈ dotblas "i-eigen dot wrong?"
  # br[9,i] = n_gflop / @belapsed dot($a, $a)
end

totwotuple(i::Int) = (i, i)
totwotuple(i::Tuple{Int,Int}) = i
function gemv_bench!(br, x, A, y, i)
  M, N = size(A)
  n_gflop = M * N * 2e-9
  xblas = A * y
  br[1, i] = n_gflop / @belapsed jgemvavx!($x, $A, $y)
  @assert x ≈ xblas "LoopVec wrong?"
  fill!(x, NaN)
  br[2, i] = n_gflop / @belapsed jgemv!($x, $A, $y)
  @assert x ≈ xblas "Julia wrong?"
  fill!(x, NaN)
  br[3, i] = n_gflop / @belapsed cgemv!($x, $A, $y)
  @assert x ≈ xblas "Clang wrong?"
  fill!(x, NaN)
  br[4, i] = n_gflop / @belapsed fgemv!($x, $A, $y)
  @assert x ≈ xblas "Fort wrong?"
  fill!(x, NaN)
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icgemv!($x, $A, $y)
    @assert x ≈ xblas "icc wrong?"
    fill!(x, NaN)
    br[6, i] = n_gflop / @belapsed ifgemv!($x, $A, $y)
    @assert x ≈ xblas "ifort wrong?"
    fill!(x, NaN)
  end
  br[5+2INTEL_BENCH, i] = n_gflop / @belapsed egemv!($x, $A, $y)
  @assert x ≈ xblas "eigen wrong?"
  fill!(x, NaN)
  br[6+2INTEL_BENCH, i] = n_gflop / @belapsed iegemv!($x, $A, $y)
  @assert x ≈ xblas "i-eigen wrong?"
  fill!(x, NaN)
  br[7+2INTEL_BENCH, i] = n_gflop / @belapsed fgemv_builtin!($x, $A, $y)
  @assert x ≈ xblas "Fort wrong?"
  fill!(x, NaN)
  br[8+2INTEL_BENCH, i] = n_gflop / @belapsed ifgemv_builtin!($x, $A, $y)
  @assert x ≈ xblas "ifort wrong?"
  fill!(x, NaN)
  br[9+2INTEL_BENCH, i] = n_gflop / @belapsed dgemvopenblas!($x, $A, $y)
  @assert x ≈ xblas "gemvopenblas wrong?"
  fill!(x, NaN)
  br[10+2INTEL_BENCH, i] = n_gflop / @belapsed dgemvmkl!($x, $A, $y)
  @assert x ≈ xblas "gemvmkl wrong?"
  fill!(x, NaN)
end
function A_mul_vb_bench!(br, s, i)
  M, N = totwotuple(s)
  x = Vector{Float64}(undef, M)
  A = rand(M, N)
  y = rand(N)
  gemv_bench!(br, x, A, y, i)
end
function At_mul_vb_bench!(br, s, i)
  M, N = totwotuple(s)
  x = Vector{Float64}(undef, M)
  A = rand(N, M)'
  y = rand(N)
  gemv_bench!(br, x, A, y, i)
end

function dot3_bench!(br, s, i)
  M, N = totwotuple(s)
  x = rand(M)
  A = rand(M, N)
  y = rand(N)
  dotblas = dot(x, A, y)
  n_gflop = M * N * 3e-9
  br[1, i] = n_gflop / @belapsed jdot3avx($x, $A, $y)
  @assert jdot3avx(x, A, y) ≈ dotblas "LoopVec dot wrong?"
  br[2, i] = n_gflop / @belapsed jdot3($x, $A, $y)
  @assert jdot3(x, A, y) ≈ dotblas "Julia dot wrong?"
  br[3, i] = n_gflop / @belapsed cdot3($x, $A, $y)
  @assert cdot3(x, A, y) ≈ dotblas "Clang dot wrong?"
  br[4, i] = n_gflop / @belapsed fdot3($x, $A, $y)
  @assert fdot3(x, A, y) ≈ dotblas "Fort dot wrong?"
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icdot3($x, $A, $y)
    @assert icdot3(x, A, y) ≈ dotblas "icc dot wrong?"
    br[6, i] = n_gflop / @belapsed ifdot3($x, $A, $y)
    @assert ifdot3(x, A, y) ≈ dotblas "ifort dot wrong?"
  end
  br[5+2INTEL_BENCH, i] = n_gflop / @belapsed edot3($x, $A, $y)
  @assert edot3(x, A, y) ≈ dotblas "eigen dot wrong?"
  br[6+2INTEL_BENCH, i] = n_gflop / @belapsed iedot3($x, $A, $y)
  @assert iedot3(x, A, y) ≈ dotblas "c-eigen dot wrong?"
  br[7+2INTEL_BENCH, i] = n_gflop / @belapsed dot($x, $A, $y)
end
# BLAS.set_num_threads(1)
function sse!(Xβ, y, X, β)
  dgemvmkl!(copyto!(Xβ, y), X, β, 1.0, -1.0)
  jdot(Xβ, Xβ)
end
sse_totwotuple(s::NTuple{2}) = s
sse_totwotuple(s::Integer) = ((3s) >> 1, s >> 1)

function sse_bench!(br, s, i)
  N, P = sse_totwotuple(s)
  y = rand(N)
  β = rand(P)
  X = randn(N, P)
  Xβ = similar(y)
  lpblas = sse!(Xβ, y, X, β)
  n_gflop = 2e-9 * (P * N + 2N)
  br[1, i] = n_gflop / @belapsed jOLSlp_avx($y, $X, $β)
  @assert jOLSlp_avx(y, X, β) ≈ lpblas "LoopVec wrong?"
  br[2, i] = n_gflop / @belapsed jOLSlp($y, $X, $β)
  @assert jOLSlp(y, X, β) ≈ lpblas "Julia wrong?"
  br[3, i] = n_gflop / @belapsed cOLSlp($y, $X, $β)
  @assert cOLSlp(y, X, β) ≈ lpblas "Clang wrong?"
  br[4, i] = n_gflop / @belapsed fOLSlp($y, $X, $β)
  @assert fOLSlp(y, X, β) ≈ lpblas "Fort wrong?"
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icOLSlp($y, $X, $β)
    @assert icOLSlp(y, X, β) ≈ lpblas "icc wrong?"
    br[6, i] = n_gflop / @belapsed ifOLSlp($y, $X, $β)
    @assert ifOLSlp(y, X, β) ≈ lpblas "ifort wrong?"
  end
  br[5+2INTEL_BENCH, i] = n_gflop / @belapsed eOLSlp($y, $X, $β)
  @assert eOLSlp(y, X, β) ≈ lpblas "eigen wrong?"
  br[6+2INTEL_BENCH, i] = n_gflop / @belapsed ieOLSlp($y, $X, $β)
  @assert ieOLSlp(y, X, β) ≈ lpblas "i-eigen wrong?"
  if MKL_BENCH
    br[7+2INTEL_BENCH, i] = n_gflop / @belapsed sse!($Xβ, $y, $X, $β)
    @assert sse!(Xβ, y, X, β) ≈ lpblas "MKL wrong?"
  end
end

function exp_bench!(br, s, i)
  a = rand(s)
  b = similar(a)
  n_gflop = 1e-9 * s # not really gflops
  br[1, i] = n_gflop / @belapsed @turbo @. $b = exp($a)
  baseb = copy(b)
  br[2, i] = n_gflop / @belapsed @. $b = exp($a)
  @assert b ≈ baseb "LoopVec wrong?"
  br[3, i] = n_gflop / @belapsed cvexp!($b, $a)
  @assert b ≈ baseb "Clang wrong?"
  br[4, i] = n_gflop / @belapsed fvexp!($b, $a)
  @assert b ≈ baseb "Fort wrong?"
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icvexp!($b, $a)
    @assert b ≈ baseb "icc wrong?"
    br[6, i] = n_gflop / @belapsed ifvexp!($b, $a)
    @assert b ≈ baseb "ifort wrong?"
  end
end

function aplusBc_bench!(br, s, i)
  M, N = totwotuple(s)
  a = rand(M)
  B = rand(M, N)
  c = rand(N)
  c′ = c'
  D = similar(B)
  n_gflop = 2e-9 * M * N
  br[1, i] = n_gflop / @belapsed @turbo @. $D = $a + $B * $c′
  Dcopy = copy(D)
  fill!(D, NaN)
  br[2, i] = n_gflop / @belapsed @. $D = $a + $B * $c′
  @assert D ≈ Dcopy "LoopVec wrong?"
  br[3, i] = n_gflop / @belapsed caplusBc!($D, $a, $B, $c)
  @assert D ≈ Dcopy "Clang wrong?"
  fill!(D, NaN)
  br[4, i] = n_gflop / @belapsed faplusBc!($D, $a, $B, $c)
  @assert D ≈ Dcopy "Fort wrong?"
  fill!(D, NaN)
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icaplusBc!($D, $a, $B, $c)
    @assert D ≈ Dcopy "icc wrong?"
    fill!(D, NaN)
    br[6, i] = n_gflop / @belapsed ifaplusBc!($D, $a, $B, $c)
    @assert D ≈ Dcopy "ifort wrong?"
    fill!(D, NaN)
  end
  br[7, i] = n_gflop / @belapsed eaplusBc!($D, $a, $B, $c)
  @assert D ≈ Dcopy "eigen wrong?"
  fill!(D, NaN)
  br[8, i] = n_gflop / @belapsed ieaplusBc!($D, $a, $B, $c)
  @assert D ≈ Dcopy "i-eigen wrong?"
  fill!(D, NaN)
end

function AplusAt_bench!(br, s, i)
  A = rand(s, s)
  B = similar(A)
  n_gflop = 1e-9 * s^2
  br[1, i] = n_gflop / @belapsed @turbo @. $B = $A + $A'
  baseB = copy(B)
  fill!(B, NaN)
  br[2, i] = n_gflop / @belapsed @. $B = $A + $A'
  @assert B ≈ baseB "LoopVec wrong?"
  br[3, i] = n_gflop / @belapsed cAplusAt!($B, $A)
  @assert B ≈ baseB "Clang wrong?"
  fill!(B, NaN)
  br[4, i] = n_gflop / @belapsed fAplusAt!($B, $A)
  @assert B ≈ baseB "Fort wrong?"
  fill!(B, NaN)
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icAplusAt!($B, $A)
    @assert B ≈ baseB "icc wrong?"
    fill!(B, NaN)
    br[6, i] = n_gflop / @belapsed ifAplusAt!($B, $A)
    @assert B ≈ baseB "ifort wrong?"
    fill!(B, NaN)
  end
  br[5+2INTEL_BENCH, i] = n_gflop / @belapsed eAplusAt!($B, $A)
  @assert B ≈ baseB "eigen wrong?"
  fill!(B, NaN)
  br[6+2INTEL_BENCH, i] = n_gflop / @belapsed ieAplusAt!($B, $A)
  @assert B ≈ baseB "i-eigen wrong?"
  fill!(B, NaN)
  br[7+2INTEL_BENCH, i] = n_gflop / @belapsed fAplusAt_builtin!($B, $A)
  @assert B ≈ baseB "Fort-builtin wrong?"
  fill!(B, NaN)
  if INTEL_BENCH
    br[8+2INTEL_BENCH, i] = n_gflop / @belapsed ifAplusAt_builtin!($B, $A)
    @assert B ≈ baseB "ifort-builtin wrong?"
    fill!(B, NaN)
  end
end

function randomaccess_bench!(br, s, i)
  A, C = totwotuple(s)
  P = rand(A, C) .+= 0.5
  basis = rand(1:C, A, C)
  coefs = randn(C)
  n_gflop = 1e-9 * (A * C + C)
  p = randomaccess(P, basis, coefs)
  br[1, i] = n_gflop / @belapsed randomaccessavx($P, $basis, $coefs)
  @assert p ≈ randomaccessavx(P, basis, coefs) "LoopVec wrong?"
  br[2, i] = n_gflop / @belapsed randomaccess($P, $basis, $coefs)
  br[3, i] = n_gflop / @belapsed crandomaccess($P, $basis, $coefs)
  @assert p ≈ crandomaccess(P, basis, coefs) "Clang wrong?"
  br[4, i] = n_gflop / @belapsed frandomaccess($P, $basis, $coefs)
  @assert p ≈ frandomaccess(P, basis, coefs) "Fort wrong?"
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icrandomaccess($P, $basis, $coefs)
    @assert p ≈ icrandomaccess(P, basis, coefs) "icc wrong?"
    br[6, i] = n_gflop / @belapsed ifrandomaccess($P, $basis, $coefs)
    @assert p ≈ ifrandomaccess(P, basis, coefs) "ifort wrong?"
  end
end

function logdettriangle_bench!(br, s, i)
  S = randn(s, 2s)
  U = cholesky(Symmetric(S * S')).U
  n_gflop = 1e-9 * s
  ld = logdet(U)
  br[1, i] = n_gflop / @belapsed jlogdettriangleavx($U)
  @assert ld ≈ jlogdettriangleavx(U) "LoopVec wrong?"
  br[2, i] = n_gflop / @belapsed jlogdettriangle($U)
  @assert ld ≈ jlogdettriangle(U) "Julia wrong?"
  br[3, i] = n_gflop / @belapsed clogdettriangle($U)
  @assert ld ≈ clogdettriangle(U) "Clang wrong?"
  br[4, i] = n_gflop / @belapsed flogdettriangle($U)
  @assert ld ≈ flogdettriangle(U) "Fort wrong?"
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed iclogdettriangle($U)
    @assert ld ≈ iclogdettriangle(U) "icc wrong?"
    br[6, i] = n_gflop / @belapsed iflogdettriangle($U)
    @assert ld ≈ iflogdettriangle(U) "ifort wrong?"
  end
  # br[7,i] = n_gflop / @belapsed elogdettriangle($U)
  # @assert ld ≈ elogdettriangle(U) "eigen wrong?"; fill!(B, NaN);
  # br[8,i] = n_gflop / @belapsed ielogdettriangle($U)
  # @assert ld ≈ ielogdettriangle(U) "i-eigen wrong?"; fill!(B, NaN);
  br[5+2INTEL_BENCH, i] = n_gflop / @belapsed logdet($U)
end

function filter2d_bench_run!(br, s, i, K)
  A = rand(s + 2, s + 2)
  B = OffsetArray(similar(A, (s, s)), 1, 1)
  Mk, Nk = size(K)
  n_gflop = 1e-9 * (2Mk * Nk - 1) * s^2
  br[1, i] = n_gflop / @belapsed filter2davx!($B, $A, $K)
  Bcopy = copy(B)
  fill!(B, NaN)
  br[2, i] = n_gflop / @belapsed filter2d!($B, $A, $K)
  @assert B ≈ Bcopy "LoopVec wrong?"
  br[3, i] = n_gflop / @belapsed cfilter2d!($B, $A, $K)
  @assert B ≈ Bcopy "Clang wrong?"
  br[4, i] = n_gflop / @belapsed ffilter2d!($B, $A, $K)
  @assert B ≈ Bcopy "Fort wrong?"
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icfilter2d!($B, $A, $K)
    @assert B ≈ Bcopy "icc wrong?"
    br[6, i] = n_gflop / @belapsed iffilter2d!($B, $A, $K)
    @assert B ≈ Bcopy "ifort wrong?"
  end
end

function filter2dunrolled_bench_run!(br, s, i, K)
  A = rand(s + 2, s + 2)
  B = OffsetArray(similar(A, (s, s)), 1, 1)
  Mk, Nk = size(K)
  n_gflop = 1e-9 * (2Mk * Nk - 1) * s^2
  br[1, i] = n_gflop / @belapsed filter2dunrolledavx!($B, $A, $K)
  Bcopy = copy(B)
  fill!(B, NaN)
  br[2, i] = n_gflop / @belapsed filter2dunrolled!($B, $A, $K)
  @assert B ≈ Bcopy "LoopVec wrong?"
  br[3, i] = n_gflop / @belapsed cfilter2dunrolled!($B, $A, $K)
  @assert B ≈ Bcopy "Clang wrong?"
  br[4, i] = n_gflop / @belapsed ffilter2dunrolled!($B, $A, $K)
  @assert B ≈ Bcopy "Fort wrong?"
  if INTEL_BENCH
    br[5, i] = n_gflop / @belapsed icfilter2dunrolled!($B, $A, $K)
    @assert B ≈ Bcopy "icc wrong?"
    br[6, i] = n_gflop / @belapsed iffilter2dunrolled!($B, $A, $K)
    @assert B ≈ Bcopy "ifort wrong?"
  end
end
