1

I have a piece of pthread code listed as the function "thread" here. It basically creates a number of threads (usually 240 on Xeon Phi and 16 on CPU) and then join them.

If I call this thread() only once, it works perfectly on both CPU and Xeon Phi. If I call it one more time, it still works fine on CPU but the pthread_create() will report "error 22" which should be "invalid argument" every 60 threads.

For example, thread 0, thread 60, thread 120 and so on of the 2nd run of thread() which are also the 241, 301, 361 and so on threads ever created in the process would fail (error 22). But thread 1~59, 61~119, 121~240, and so on work perfectly.

Note that this problem happens only on Xeon Phi.

I have checked the stack sizes, and the argument themselves, but I didn't find the reason for this. The arguments are correct.

void thread()
{

...

int i, rv;
cpu_set_t set;
arg_t args[nthreads];
pthread_t tid[nthreads];
pthread_attr_t attr;
pthread_barrier_t barrier;

rv = pthread_barrier_init(&barrier, NULL, nthreads);
if(rv != 0)
{
    printf("Couldn't create the barrier\n");
    exit(EXIT_FAILURE);
}

pthread_attr_init(&attr);

for(i = 0; i < nthreads; i++)
{
    int cpu_idx = get_cpu_id(i,nthreads);

    DEBUGMSG(1, "Assigning thread-%d to CPU-%d\n", i, cpu_idx);

    CPU_ZERO(&set);
    CPU_SET(cpu_idx, &set);
    pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &set);

    args[i].tid = i;
    args[i].ht = ht;
    args[i].barrier = &barrier;

    /* assing part of the relR for next thread */
    args[i].relR.num_tuples = (i == (nthreads-1)) ? numR : numRthr;
    args[i].relR.tuples = relR->tuples + numRthr * i;
    numR -= numRthr;

    /* assing part of the relS for next thread */
    args[i].relS.num_tuples = (i == (nthreads-1)) ? numS : numSthr;
    args[i].relS.tuples = relS->tuples + numSthr * i;

    numS -= numSthr;

    rv = pthread_create(&tid[i], &attr, npo_thread, (void*)&args[i]);
    if (rv)
    {
        printf("ERROR; return code from pthread_create() is %d\n", rv);
        printf ("%d %s\n", args[i].tid, strerror(rv));
        //exit(-1);
    }

}

for(i = 0; i < nthreads; i++)
{
    pthread_join(tid[i], NULL);
    /* sum up results */
    result += args[i].num_results;
}
}
thierry
  • 217
  • 2
  • 12
  • 1
    What is `nthreads`? How do you create the `tid` and `args` arrays? Any chance of a complete test case? – Mike Seymour Nov 13 '14 at 07:53
  • @MikeSeymour nthreads is the number of threads. It is 240 when the reported problem happens on Xeon Phi. I added the declarations of tid and args. – thierry Nov 13 '14 at 07:55
  • Give us a full program we can compile. – John Zwinck Nov 13 '14 at 08:27
  • Thanks. To run this code, you need a Xeon Phi card. You can reproduce this bug by cloning this code: https://github.com/saurabhjha1/mic-hashjoin and duplicate line 197 of main.c. Most parts of the code are irrelevant. Any idea about why pthread_create() reports "invalid argument" periodically when the arguments are ok? @JohnZwinck – thierry Nov 13 '14 at 08:47
  • What does `get_cpu_id` do and does `phtread_attr_setaffinity_np` return an error? Why do you change the affinity in `attr` so many times when you create all the threads with the `attr` from the last iteration of the first loop? – Art Nov 13 '14 at 08:48
  • AH! Sorry, I mistakenly placed a "for" here. Now the code is corrected. get_cpu_id and phtread_attr_setaffinity_np are all good. They set the affinity of each thread. @Art – thierry Nov 13 '14 at 08:54
  • @thierry: Is it still broken now? In the code you provided I don't see anything that would require a Xeon Phi. Can you post here a minimal, complete example program that exhibits the bug on your Phi and we can test it? Or better yet, you can test it on a regular x86 system. Also, it's suspicious that every 60th thread fails, since Phi is a 60-core computer. – John Zwinck Nov 13 '14 at 08:56
  • 3
    I'm looking at `get_cpu_id` in scatter_threads.h and it looks like it will return random garbage when `front` or `back` overflow. Also since the behavior of that function is affected by a magic constant with the value "60" and you have problem when reaching 60 calls to that function I'd suspect it's not "all good". – Art Nov 13 '14 at 08:57
  • @Art: the magic constant `60` is the number of cores in the Xeon Phi. – John Zwinck Nov 13 '14 at 08:57
  • 2
    Same thing goes for `get_cpu_id` in balanced_threads.h. It doesn't check for overflow of `mfree` and will start to read, overwrite and return random things when that happens. I'd start looking at those functions. Maybe `setaffinity_np` doesn't check for too high bits set in the cpu_set and that error doesn't get detected until `pthread_create` is called with bogus attributes. – Art Nov 13 '14 at 09:03
  • @didierc Sorry, I didn't state it clearly. This problem only happens on Xeon Phi. This code works perfectly on CPU. – thierry Nov 13 '14 at 09:04
  • @JohnZwinck It works perfectly on X86 CPUs but only has this problem on Xeon Phi. Yes, the Xeon Phi card I am using has 60 cores. But in the first run of this piece code, everything is fine. When the process runs this piece of pthread code for the 2nd time, this problem happens. So I think the code maybe OK but some environment setting or pthread setting is not correct. – thierry Nov 13 '14 at 09:07

1 Answers1

5

Here's a minimal example to reproduce your problem and show where your code most likely goes wrong:

#define _GNU_SOURCE
#include <pthread.h>
#include <err.h>
#include <stdio.h>

void *
foo(void *v)
{
        printf("foo\n");
        return NULL;
}

int
main(int argc, char **argv)
{
        pthread_attr_t attr;
        pthread_t thr;
        cpu_set_t set;
        void *v;
        int e;

        if (pthread_attr_init(&attr))
                err(1, "pthread_attr_init");
        CPU_ZERO(&set);
        CPU_SET(255, &set);
        if (pthread_attr_setaffinity_np(&attr, sizeof(set), &set))
                err(1, "pthread_attr_setaffinity_np");

        if ((e = pthread_create(&thr, &attr, foo, NULL)))
                errx(1, "pthread_create: %d", e);

        if (pthread_join(thr, &v))
                err(1, "pthread_join");
        return 0;
}

As I speculated in the comments to your question, pthread_attr_setaffinity_np doesn't check if the cpu set is sane. Instead that error gets caught in pthread_create. Since the cpu_get_id functions in your code on github are obviously broken, that's where I'd start looking for the problem.

Tested on Linux, but that's where pthread_attr_setaffinity_np comes from, so it's probably a safe assumption.

Art
  • 19,807
  • 1
  • 34
  • 60
  • Thanks very much! That's the problem! I rewrote the scheduler part and it's now ok! Thanks. – thierry Nov 13 '14 at 09:18
  • p.s. Unless you have looked very carefully, your assumptions about which logicalCPUs are close to each other on Xeon Phi are likely to be wrong. The mapping from logicalCPU to hardware is *not* the same as that on the "normal" Xeons (which is perverse enough in itself :-)) – Jim Cownie Nov 13 '14 at 22:53