Как напечатать прогресс прохождения Cython? - PullRequest
1 голос
/ 27 апреля 2020

Я использую prange для распараллеливания al oop в моем коде Cython. Поскольку это l oop занимает много времени, я хочу напечатать его прогресс по ходу дела. Индикатор выполнения был бы хорош, но все, что показывает прогресс, сделало бы. Я попытался добавить какую-либо форму регистрации следующим образом:

for index in prange(n, nogil = True, num_threads = 5):
    if index == (index//1000)*1000:
        printf("%d percent done", index*100/n)

Это должно печатать прогресс всякий раз, когда index % 1000 == 0. Вывод этого является случайным.

0 percent done
80 percent done
20 percent done
100 percent done
60 percent done

Я предполагаю, что это потому, что prange не назначает потоки индексам, начиная с 0 и повышаясь. Есть ли способ реализовать такую ​​вещь в Cython? Спасибо!

1 Ответ

0 голосов
/ 28 апреля 2020

Возможно, в Cython есть способы достичь того, чего вы хотите, однако я считаю, что проще использовать OpenMP из C / C ++ и вызывать функциональность из Cython (благодаря C -verbatim-code начиная с Cython 0.28 это довольно удобно).

Ясно одно: для достижения вашей цели вам нужна какая-то синхронизация между потоками, которая может повлиять на производительность. Моя стратегия проста: каждый поток сообщает «выполнено», все выполненные задачи регистрируются, и их количество время от времени сообщается. Количество зарегистрированных задач защищено с помощью мьютекса / блокировки :

%%cython -c=/openmp --link-args=/openmp
cdef extern from * nogil:
    r"""
    #include <omp.h>
    #include <stdio.h>

    static omp_lock_t cnt_lock;
    static int cnt = 0;
    void reset(){
       omp_init_lock(&cnt_lock);
       cnt = 0;
    }
    void destroy(){
      omp_destroy_lock(&cnt_lock);
    }

    void report(int mod){
        omp_set_lock(&cnt_lock);
        // start protected code:
        cnt++;
        if(cnt%mod == 0){
           printf("done: %d\n", cnt);
        }
        // end protected code block
        omp_unset_lock(&cnt_lock);
    }
    """
    void reset()
    void destroy()
    void report(int mod)


from cython.parallel import prange
def prange_with_report(int n):   
    reset() # reset counter and init lock
    cdef int index
    for index in prange(n, nogil = True, num_threads = 5):
        report(n//10)
    destroy() # release lock

Теперь вызов: prange_with_report(100) напечатает done: 10\n, done: 20\n, ..., done: 100\n.

В качестве небольшой оптимизации report можно вызывать не для каждого index, а только, например, для index%100==0 - это будет меньше влиять на производительность, но и отчетность будет менее точной .

...