List of pragmas

Where does one find a list of pragmas and explanations of what they achieve?

In particular, I see

pragma "fn synchronization free"

before calls to extern C functions? What does that do? Why is is needed?

Thanks - heaps

Hi Damian -- these pragmas are not intended to be user facing and are only documented in the compiler source code. The pragma you are seeing here appears in the Math module because it allows the forall-unordered optimization to run even if the loop body has a call to one of these functions. You can see this optimization described on slide 27 in https://chapel-lang.org/presentations/ChapelForCharmxx.pdf , for example. The compiler needs to know that the extern procs aren't implementing synchronization between tasks in the forall loop, and that is what the pragma is about.

In my view we would use an attribute syntax generalized attribute syntax · Issue #14141 · chapel-lang/chapel · GitHub to make this kind of idea user facing if that proves to be needed.

Hi Damian —

Chapel's pragmas aren't intended to be user-facing (they're typically ways
of bootstrapping things in internal modules or working around the lack of
a feature). As a result, they aren't really documented (even for
developers, unfortunately). In practice, most of us learn about them
through word-of-mouth from other developers or by reading the commit
messages that introduced them.

If there's a specific capability you (or a user) are needing and lacking,
I'd suggest asking about it rather than searching for a pragma that will
do what you need.

-Brad

Hi Michael, Brad - If I am calling C routines, it seems like this pragma is necessary all the time. (BTW, thanks for the reference. I assume you mean the stuff on Page 27. Nice presentation by the way. Looks like GPU stuff is moving ahead in leaps and bounds - nice work)

As an example, consider the computation of the complex projection (of a point in the complex plane onto the surface of a Riemann Sphere) as an attribute of a variable. If I write this naively, it could be:

inline proc (complex(?w)).proj : real(w / 2)
{
    extern proc cprojf(z: complex(64)): real(32);
    extern proc cproj(z: complex(128)): real(64);

    return if w == 64 then cprojf(this) else cproj(this);
}

Referring to the routine cproj() in Math.chpl that does the same function, both those C routines are prefixed by

    pragma "fn synchronization free"

It seems that I need to put this in front of every simple C routine that I use to tell the Chapel compiler that it is not doing task synchronization. Or am I incorrect?

For completeness, but totally aside from the pragma issue ....,

I could avoid C altogether and use current features of Chapel to write (although I have not debugged this)

inline proc (complex(?w)).proj 
{
    const z = this;
    const re = z.re, im = z.im;
    type R = re.type, C = z.type;
    param inf = INFINITY:R, zero = 0:R;

    return if abs(re) == inf || abs(im) == inf then
       (inf, if im < zero then +zero else -zero):C
    else
        z;
}

In practice, that has IEEE 754 exception issues so I might even use my own ieee754 module which resolves them and avoids that cast seen above.

You would have to have this pragma for the forall-unordered optimization to fire if the function in question is called in the loop body. But that is an optimization and it might not be important at all in practice for your application. This optimization only applies to distributed memory. It won't impact correctness and it won't impact performance of loops that are completely local. So, I'd recommend you not worry about the pragma for now.

Like I said, in the future, I'd like for users to be able to communicate such information about an extern proc, with a different means, but I don't think we're ready to build that right now.

1 Like

Thanks for the lucid explanation Michael.

It appears certain that I do not have to worry about that pragma for now.

I like the idea of a mechanism that is different to a pragma.

1 Like

Why does the cproj() function in Math.chpl return a real(w/2). It should return a complex(w) number. The C routines that this routines call return complex, not float or double numbers.

That's a very good question. It looks like it "works", we have a test
that uses that function. I suspect it drops part of the result on the
floor. Would you be up for opening an issue on that?

Thanks,
Lydia

This looks like a transcription error to me, thanks for pointing it out. Tagging @diten who did the implementation to see if he agrees.

-Brad

I agree that it looks like a transcription error. It should return a complex(w) as Damian pointed out.

David

I could open an issue but before that

  • the imaginary part dropped on the floor is +/-0.0 i (but the sign is important to some people)
  • one could just rewrite the whole thing in, i.e. cproj directly in Chapel.

You can use that method-on-a-complex I wrote earlier as the basis for the standalone routine.

Or is that a bad suggestion?

I don't think it's a bad suggestion, but there's a number of factors
we'd want to consider:

  • Does it provide something that using the C version doesn't?
  • Are there optimizations that would be available on the C version that
    writing it in Chapel would accidentally avoid?
  • Are there cases where the behavior would differ? If so, are we okay
    with those differences?

Lydia

On Math module - should we rename conjg? · Issue #19010 · chapel-lang/chapel · GitHub, Damian said:

Strictly speaking, cproj() in Math.chpl has a bug if you want to be IEEE 754 strict or if you are trying to port existing C/C++ routines.

Damian, is this why you suggest rewriting directly in Chapel?

I'd forgotten that this routine existed earlier in the thread (I'd forgotten how this thread started by this point), so here's the link in case anyone else was in the same boat: List of pragmas - #5 by damianmoz

I'm not a fan of the method-on-type approach for math functions over standalone functions, but I'm not opposed to changing that and taking the rest if this is the reason Damian's suggesting not just calling into C.

-Brad

Thanks for those ideas Lydia. I like those ideas:

  • It can be written in line - I assumed that the code in Math.chpl using the routine from the C library would not be pulled in-line;
  • Have a look at the code posted earlier - it is really simple - if the compiler cannot optimize this as well as C, somebody needs to spend more time working on the optimizer - hey - a good test case Michael!!
  • Have a look at the code I posted - as far as I know, the behaviour matches what the POSIX routine says it should do - or if not I, or somebody smarter than me, needs to fix my buggy Chapel code. The POSIX code definition is a one-liner!

What else did I miss? It cannot be that easy

Brad, the bug is in the way the original Chapel was written. It should not return a real(w / 2). Just rip that out and I assume the problem is fixed. The C routines called by the Chapel cproj return a C complex number return complex numbers and limiting the return type to real(w / 2) discards the imaginary part which is wrong, at least according to the POSIX definition.

Please note that the code I first posted was a copy-and-paste nightmare. Here is it, hopefully correctly hacked but not tested to work as a conventional proc:

inline proc cproj(z : complex(?w))
{
    const re = z.re, im = z.im;
    type R = re.type, C = z.type;
    param inf = INFINITY:R, zero = 0:R;

    return if abs(re) == inf || abs(im) == inf then
       (inf, if im == zero then im else if im > zero then zero else -zero):C
    else
        z;
}

I only suggested writing it in Chapel because that way, in the original context of this post, Chapel could see the whole routine and there would be no need for those pragmas about which I asked. Also, the original Chapel called both a float routine cprojf() and a double cproj(). I could not figure out how Chapel was smart enough to get the return type right with a single routine. I have always had to use a where clause to qualify my routine to get that right. So, writing it in Chapel generically was the lazy way out.

I was not suggesting that you use a method-on-type to implement the fix. I had that code in one of my modules and I knew it worked so I did a cut and past. Mind you, the first attempt was awful. My approach was a method-on-type so it did not clash name-wise with the version in Math.

I have implemented two modules with method-on-types, but also method-on-expressions. I like this approach for what are inherently a property of a type or an expression of a given type. So, good or bad as it may be, my criteria for what fits this approach would say that the bit pattern of a floating point number does, as would the arg[ument] or mod[ulus] of a complex number, the former and latter being what some people call respectively the phase and absolute value of a complex number. Extrapolating this, I would go so far as to say that the absolute value is also a property of a real(w) type, as is the complex conjugate conj or conjg (just an partial negation), or cproj which for most complex numbers is just a mapping of a finite complex number to either itself. Only when one of the components is an infinite do you map it to a complex number with only its real component being infinite, the imaginary part being +/- 0.0.

Certainly, given x as a floating point number, I would never say that sin(x) is a property of its argument. That is taking the object oriented approach too far.

Using the method-on-type approach also has the advantage of using a name-space that is extremely small and has no risk of a namespace clash with something else and I get to choose all my own names. One of the modules mentioned is some simple stuff for use with complex numbers like what you saw in my original example above. When ready for prime time, the other will eventually be a total, but not drop-in, replacement for the features found in C's float.h or C++'s <numeric_limits> template class.

1 Like

Sorry for my awful typing. I have edited/fixed many typos in my previous post, done too quickly, expanded some explanations in that same post, and also fixed my fat fingered typo in the earlier Chapel code that appeared as the 2nd post within this topic.

1 Like

In terms of method-on-type, I am not proposing we all stop using abs(x), or start using x.abs instead of it. In case I misrepresented that previously. I was trying to be complete in my explanation.

1 Like

Ah, I misunderstood, sorry. For some reason, I thought you were saying that the C cproj function had a bug. I think I must've misread Math.chpl as Math.h.

-Brad

I've filed Fix simple problem with cproj() pointed out by Damian by bradcray · Pull Request #19142 · chapel-lang/chapel · GitHub to address this.

-Brad

I cannot see that issue yet in the normal way I look at Github. But if I click the link, I can see something. Maybe it needs a box ticked before us mere mortals can see it.

I should point out that my implementation does not handle NaN correctly but I was trying not introduce too many concepts in one hit. To do that needs a Chapel implementation of copysign(x,y), a C routine (which is also a generic macro in later versions of C and C++). To do that for all cases needs Github Issue #14140 addressed (I am about to offer my 2c on that but the complete fix is beyond my pay grade as it needs somebody much compiler savvy and smarter than I am).

Also, in terms of performance which Lydia highlighted, I was assuming that the current Chapel code would have called the external C routines, cprojf() or cproj(). If the underlying compiler backend is effectively using the C or C++ generic macro version of those two C routines, it might be a neck-and-neck tussle to see whether the Chapel-optimised inline version is better than the C version. In that case, I would get on the C version. Mind you, the race would swing back to Chapel's favour with a good Chpel implementation of copysign() or copySign() or whatever name it is given. Remember that this IEEE754 compliant routine copies the negative bit (as there is no such thing as a sign bit) and even an NaN has a negative bit. Anyway, the optimization is a job for compiler gurus and is out of my skill set.

I will wait until the Github issues percolates out of the pull process to add any more.