constexpr constructor and compile time execution

Issue #208 resolved
Astor created an issue

Hi!

First of all let me thank you for providing such a great numerical library to the community.

As a part of our research we aim to implement complex algorithms to solve systems of equations at compile time with GCC 8.2.

Let me include a simple example here:

#include <array>
template <std::size_t nr, std::size_t nc, typename number>
constexpr std::array<number,nr>
operator *(const std::array<std::array<number,nc>,nr> & A,const std::array<number,nc> & x)
{
  std::array<number,nr> res{} ;
  for (auto i=0u;i<nr;++i)
    for (auto j=0u;j<nc;++j)
      res[i] += A[i][j]*x[j];
  return res ;
}

constexpr auto mymain()
{
  const std::array<double,4> x{1.,2.,3.,4.};
  const std::array<std::array<double,4>,4> A{std::array<double,4>{1.,2.,3.,4.},
                                             std::array<double,4>{5.,6.,7.,8.},
                                             std::array<double,4>{9.,10.,11.,12.},
                                             std::array<double,4>{13.,14.,15.,16.}};
  const std::array<double,4> res = A*x;
  return res ;
}

int main(int argc, char *argv[])
{
  const volatile auto res = mymain();
  return 0;
}

Such a code generates an assembly containing the result res in the data section:

  22                            .data
  23                            .align 32
  26                    res.31806:
  27 0000 00000000              .long   0
  28 0004 00003E40              .long   1077805056
  29 0008 00000000              .long   0
  30 000c 00805140              .long   1079083008
  31 0010 00000000              .long   0
  32 0014 00805B40              .long   1079738368
  33 0018 00000000              .long   0
  34 001c 00C06240              .long   1080213504

I would love to use Blaze and all its resources for my project, a similar code written with Blaze would look like:

#include <blaze/Math.h>

constexpr auto mymain()
{
  const blaze::StaticVector<double,4> x{1.,2.,3.,4.};
  const blaze::StaticMatrix<double,4,4> A{{1.,2.,3.,4.},
                                          {5.,6.,7.,8.},
                                          {9.,10.,11.,12.},
                                          {13.,14.,15.,16.}};
  const blaze::StaticVector<double,4> res = A*x;
  return res ;
}

int main(int argc, char *argv[])
{
  const volatile auto res = mymain();
  return 0;
}

However this does not compile because StaticMatrix and StaticVector have no constexpr constructor.

Is there any interest in implementing such constructors? If there is, could I be of any help? Or, on the other hand, am I misunderstanding how Blaze should be used for compile-time calculations?

I am compiling with the following command:

g++ -I/path/to/blaze/include -march=native -std=c++1z -Ofast -DNDEBUG -ftree-vectorizer-verbose=9 -fopt-info-optall=vec_report -Wa,-adhln -g source.cc -o exec > assembly.s

Thanks for your attention.

Best!

Comments (12)

  1. Klaus Iglberger

    Hi Astor!

    Thanks a lot for creating this issue. This question has been raised before (see issue #163) and indeed this would be a logical extension for static vectors and matrices. Unfortunately we are building on intrinsics for all arithmetic operations (addition, subtraction, multiplications, etc.). These functions give us control over SIMD statements, which we need to achieve maximum performance at runtime. Unfortunately these functions are not constexpr, so cannot be used for compile time computations. Therefore we would need additional kernels for the compile time computations. Unfortunately, at this point in time it is not possible to overload on constexpr or to switch at compile time based on constexpr. Of course there would be a way to achieve the goal (for instance by overloading for StaticVector and StaticMatrix explicitly), but this would unfortunately diminish our runtime performance, which is of course highly undesirable. To our best knowledge there is currently (C++14) no way to implement the functionality you are asking for without sacrificing runtime performance.

    As also stated in issue #163 we will revisit the issue as soon as we switch to C++17 and try to enable complete compile time computation. In your particular case, however, there might be a middle way. For your purpose it would be enough if StaticVector and StaticMatrix would have constexpr constructors. Then you would be able to implement the arithmetic operations yourself. If this is a reasonable compromise, we would within the next weeks implement the necessary changes. Would this work for you?

    Best regards,

    Klaus!

  2. Astor reporter

    Hello Klaus!

    It seems to me that it would be a great compromise!

    It would give me the chance of at least get rid of my std::array implementation and fuse the code I have until now. I literally have two implementations of the same algorithms with std::array and blaze::StaticMatrix and blaze::StaticVector explicitly.

    Please let me know if I can help, I'll be ready to test it once available anyway!

  3. Klaus Iglberger

    Hi Astor!

    Commits 84730c0 and 71581c4 equip StaticVector and StaticMatrix with constexpr constructors, assignment operators and utility functions. Please give me feedback in case an operation that you expect to be constexpr is not yet declared as constexpr. I apologize for the long delay,

    Best regards,

    Klaus!

  4. Astor reporter

    Hi Klaus!

    Many thanks for your work. I tested the following code:

    #include <blaze/Math.h>
    
    template <std::size_t nr, std::size_t nc, typename number=double>
    constexpr
    inline blaze::StaticVector<number,nr>
    operator * (const blaze::StaticMatrix<number,nc,nr> & A,const blaze::StaticVector<number,nc> & x)
    {
      blaze::StaticVector<number,nr> res ;
      for (auto i=0u;i<nr;++i)
        for (auto j=0u;j<nc;++j)
          res[i] += A(i,j)*x[j];
      return res ;
    }
    
    constexpr auto mymain()
    {
      const blaze::StaticVector<double,4> x{1.,2.,3.,4.};
      const blaze::StaticMatrix<double,4,4> A{{1.,2.,3.,4.},
          {5.,6.,7.,8.},
        {9.,10.,11.,12.},
          {13.,14.,15.,16.}};
      const blaze::StaticVector<double,4> res = A*x;
      return res ;
    }
    
    int main(int argc, char *argv[])
    {
      const volatile auto res = mymain();
      return 0;
    }
    

    Please find the generated assembly attached.

    It does generate the solution vector at compile time in data section .LC0. However, I had to manually make the copy constructor constexpr in blaze/math/dense/StaticVector.h. I guess there was an interaction with the rest of the code that made it impossible to qualify it constexpr?

    I am also getting A LOT of code related to the generation of random data that I am not using (as far as I am aware). Is that normal?

    Thanks again.

    Astor

  5. Klaus Iglberger

    Hi Astor!

    Thanks for your feedback. The copy constructor is indeed a special case. I'm still running benchmarks to determine if it is possible to improve the runtime performance by explicit vectorization. In that case it unfortunately wouldn't be possible to declare the function constexpr since the necessary intrinsics (see the Intel Intrinsics Guide) are not constexpr. If I find that compilers perform the vectorization well enough on their own I can also make the copy constructor constexpr.

    As for the amount of generated code I cannot say if this is normal or not. I will have to investigate this as well. Thanks for pointing this out.

    Best regards,

    Klaus!

  6. Astor reporter

    Thanks for your answer!

    I have little hope that GCC will perform the vectorization well enough on its own, just from personal experience. For the moment though, I can work just by changing the copy constructor to constexpr myself.

    The bloating of the executable due to the usage of libraries to generate random numbers is indeed strange though, particularly when I am not using them. I haven't made any measurements yet, but having implemented my code only with STL, then with Eigen and with Blaze, it could be adding a significant amount of compile time. Again, just a hunch.

    Thanks a lot!

    Astor

  7. Klaus Iglberger

    Hi Astor!

    Commits 0684df4 and 8aea9aa add the constexpr specifier to the copy constructors and copy assignment operators of the StaticVector and StaticMatrix class templates. This hopefully resolves this issue and I apologize for the unusually long delay.

    As for the code bloat, this seems to be a more general issue that we will deal with separately.

    Best regards,

    Klaus!

  8. Klaus Iglberger

    The feature has been implemented, tested, optimized, and documented as required. It is immediately available via cloning the Blaze repository and will be officially released in Blaze 3.5.

  9. Log in to comment