1# tag: run
2
3cimport cython.parallel
4from cython.parallel import prange, threadid
5from cython.view cimport array
6from libc.stdlib cimport malloc, calloc, free, abort
7from libc.stdio cimport puts
8
9import os
10import sys
11
12try:
13    from builtins import next # Py3k
14except ImportError:
15    def next(it):
16        return it.next()
17
18#@cython.test_assert_path_exists(
19#    "//ParallelWithBlockNode//ParallelRangeNode[@schedule = 'dynamic']",
20#    "//GILStatNode[@state = 'nogil]//ParallelRangeNode")
21def test_prange():
22    """
23    >>> test_prange()
24    (9, 9, 45, 45)
25    """
26    cdef Py_ssize_t i, j, sum1 = 0, sum2 = 0
27
28    with nogil, cython.parallel.parallel():
29        for i in prange(10, schedule='dynamic'):
30            sum1 += i
31
32    for j in prange(10, nogil=True):
33        sum2 += j
34
35    return i, j, sum1, sum2
36
37def test_descending_prange():
38    """
39    >>> test_descending_prange()
40    5
41    """
42    cdef int i, start = 5, stop = -5, step = -2
43    cdef int sum = 0
44
45    for i in prange(start, stop, step, nogil=True):
46        sum += i
47
48    return sum
49
50def test_prange_matches_range(int start, int stop, int step):
51    """
52    >>> test_prange_matches_range(0, 8, 3)
53    >>> test_prange_matches_range(0, 9, 3)
54    >>> test_prange_matches_range(0, 10, 3)
55
56    >>> test_prange_matches_range(0, 10, -3)
57
58    >>> test_prange_matches_range(0, -10, -3)
59    >>> test_prange_matches_range(1, -10, -3)
60    >>> test_prange_matches_range(2, -10, -3)
61    >>> test_prange_matches_range(3, -10, -3)
62    """
63    cdef int i = -765432, range_last = -876543, prange_last = -987654
64    prange_set = set()
65    for i in prange(start, stop, step, nogil=True, num_threads=3):
66        prange_last = i
67        with gil:
68            prange_set.add(i)
69    range_set = set(range(start, stop, step))
70    assert range_set == prange_set, "missing: %s extra %s" % (sorted(range_set-prange_set), sorted(prange_set - range_set))
71    for ii in range(start, stop, step):
72        range_last = ii
73    if range_set:
74        assert prange_last == i
75        assert range_last == prange_last
76
77
78def test_propagation():
79    """
80    >>> test_propagation()
81    (9, 9, 9, 9, 450, 450)
82    """
83    cdef int i = 0, j = 0, x = 0, y = 0
84    cdef int sum1 = 0, sum2 = 0
85
86    for i in prange(10, nogil=True):
87        for j in prange(10):
88            sum1 += i
89
90    with nogil, cython.parallel.parallel():
91        for x in prange(10):
92            for y in prange(10):
93                sum2 += y
94
95    return i, j, x, y, sum1, sum2
96
97# DISABLED, not allowed in OpenMP 3.0 (fails on Windows)
98#def test_unsigned_operands():
99#    """
100#    >>> test_unsigned_operands()
101#    10
102#    """
103#    cdef int i
104#    cdef int start = -5
105#    cdef unsigned int stop = 5
106#    cdef int step = 1
107#
108#    cdef int steps_taken = 0
109#    cdef int *steps_takenp = &steps_taken
110#
111#    for i in prange(start, stop, step, nogil=True):
112#        steps_taken += 1
113#        if steps_takenp[0] > 10:
114#            abort()
115#
116#    return steps_taken
117
118def test_reassign_start_stop_step():
119    """
120    >>> test_reassign_start_stop_step()
121    20
122    """
123    cdef int start = 0, stop = 10, step = 2
124    cdef int i
125    cdef int sum = 0
126
127    for i in prange(start, stop, step, nogil=True):
128        start = -2
129        stop = 2
130        step = 0
131
132        sum += i
133
134    return sum
135
136def test_closure_parallel_privates():
137    """
138    >>> test_closure_parallel_privates()
139    9 9
140    45 45
141    0 0 9 9
142    """
143    cdef int x
144
145    def test_target():
146        nonlocal x
147        for x in prange(10, nogil=True):
148            pass
149        return x
150
151    print test_target(), x
152
153    def test_reduction():
154        nonlocal x
155        cdef int i
156
157        x = 0
158        for i in prange(10, nogil=True):
159            x += i
160
161        return x
162
163    print test_reduction(), x
164
165    def test_generator():
166        nonlocal x
167        cdef int i
168
169        x = 0
170        yield x
171        x = 2
172
173        for i in prange(10, nogil=True):
174            x = i
175
176        yield x
177
178    g = test_generator()
179    print next(g), x, next(g), x
180
181def test_closure_parallel_with_gil():
182    """
183    >>> test_closure_parallel_with_gil()
184    45
185    45
186    """
187    cdef int sum = 0
188    temp1 = 5
189    temp2 = -5
190
191    def test_reduction():
192        nonlocal sum, temp1, temp2
193
194        cdef int i
195
196        for i in prange(10, nogil=True):
197            with gil:
198                sum += temp1 + temp2 + i
199                # assert abs(sum - sum) == 0
200
201        return sum
202
203    print test_reduction()
204    print sum
205
206def test_pure_mode():
207    """
208    >>> test_pure_mode()
209    0
210    1
211    2
212    3
213    4
214    4
215    3
216    2
217    1
218    0
219    0
220    """
221    import Cython.Shadow
222    pure_parallel = sys.modules['cython.parallel']
223
224    for i in pure_parallel.prange(5):
225        print i
226
227    for i in pure_parallel.prange(4, -1, -1, schedule='dynamic', nogil=True):
228        print i
229
230    with pure_parallel.parallel():
231        print pure_parallel.threadid()
232
233cdef extern from "types.h":
234    ctypedef short actually_long_t
235    ctypedef long actually_short_t
236
237ctypedef int myint_t
238
239def test_nan_init():
240    """
241    >>> test_nan_init()
242    """
243    cdef int mybool = 0
244    cdef int err = 0
245    cdef int *errp = &err
246
247    cdef signed char a1 = 10
248    cdef unsigned char a2 = 10
249    cdef short b1 = 10
250    cdef unsigned short b2 = 10
251    cdef int c1 = 10
252    cdef unsigned int c2 = 10
253    cdef long d1 = 10
254    cdef unsigned long d2 = 10
255    cdef long long e1 = 10
256    cdef unsigned long long e2 = 10
257
258    cdef actually_long_t miss1 = 10
259    cdef actually_short_t miss2 = 10
260    cdef myint_t typedef1 = 10
261
262    cdef float f = 10.0
263    cdef double g = 10.0
264    cdef long double h = 10.0
265
266    cdef void *p = <void *> 10
267
268    with nogil, cython.parallel.parallel():
269        # First, trick the error checking to make it believe these variables
270        # are initialized after this if
271
272        if mybool: # mybool is always false!
273            a1 = a2 = b1 = b2 = c1 = c2 = d1 = d2 = e1 = e2 = 0
274            f = g = h = 0.0
275            p = NULL
276            miss1 = miss2 = typedef1 = 0
277
278        if (a1 == 10 or a2 == 10 or
279            b1 == 10 or b2 == 10 or
280            c1 == 10 or c2 == 10 or
281            d1 == 10 or d2 == 10 or
282            e1 == 10 or e2 == 10 or
283            f == 10.0 or g == 10.0 or h == 10.0 or
284            p == <void *> 10 or miss1 == 10 or miss2 == 10
285            or typedef1 == 10):
286            errp[0] = 1
287
288    cdef int i
289    for i in prange(10, nogil=True):
290        # First, trick the error checking to make it believe these variables
291        # are initialized after this if
292
293        if mybool: # mybool is always false!
294            a1 = a2 = b1 = b2 = c1 = c2 = d1 = d2 = e1 = e2 = 0
295            f = g = h = 0.0
296            p = NULL
297            miss1 = miss2 = typedef1 = 0
298
299        if (a1 == 10 or a2 == 10 or
300            b1 == 10 or b2 == 10 or
301            c1 == 10 or c2 == 10 or
302            d1 == 10 or d2 == 10 or
303            e1 == 10 or e2 == 10 or
304            f == 10.0 or g == 10.0 or h == 10.0 or
305            p == <void *> 10 or miss1 == 10 or miss2 == 10
306            or typedef1 == 10):
307            errp[0] = 1
308
309    if err:
310        raise Exception("One of the values was not initialized to a maximum "
311                        "or NaN value")
312
313    c1 = 20
314    with nogil, cython.parallel.parallel():
315        c1 = 16
316
317
318cdef void nogil_print(char *s) with gil:
319    print s.decode('ascii')
320
321def test_else_clause():
322    """
323    >>> test_else_clause()
324    else clause executed
325    """
326    cdef int i
327
328    for i in prange(5, nogil=True):
329        pass
330    else:
331        nogil_print('else clause executed')
332
333def test_prange_break():
334    """
335    >>> test_prange_break()
336    """
337    cdef int i
338
339    for i in prange(10, nogil=True):
340        if i == 8:
341            break
342    else:
343        nogil_print('else clause executed')
344
345def test_prange_continue():
346    """
347    >>> test_prange_continue()
348    else clause executed
349    0 0
350    1 0
351    2 2
352    3 0
353    4 4
354    5 0
355    6 6
356    7 0
357    8 8
358    9 0
359    """
360    cdef int i
361    cdef int *p = <int *> calloc(10, sizeof(int))
362
363    if p == NULL:
364        raise MemoryError
365
366    for i in prange(10, nogil=True):
367        if i % 2 != 0:
368            continue
369
370        p[i] = i
371    else:
372        nogil_print('else clause executed')
373
374    for i in range(10):
375       print i, p[i]
376
377    free(p)
378
379def test_nested_break_continue():
380    """
381    >>> test_nested_break_continue()
382    6 7 6 7
383    8
384    """
385    cdef int i, j, result1 = 0, result2 = 0
386
387    for i in prange(10, nogil=True, num_threads=2, schedule='static'):
388        for j in prange(10, num_threads=2, schedule='static'):
389            if i == 6 and j == 7:
390                result1 = i
391                result2 = j
392                break
393        else:
394            continue
395
396        break
397
398    print i, j, result1, result2
399
400    with nogil, cython.parallel.parallel(num_threads=2):
401        for i in prange(10, schedule='static'):
402            if i == 8:
403                break
404            else:
405                continue
406
407    print i
408
409cdef int parallel_return() nogil:
410    cdef int i
411
412    for i in prange(10):
413        if i == 8:
414            return i
415    else:
416        return 1
417
418    return 2
419
420def test_return():
421    """
422    >>> test_return()
423    8
424    """
425    print parallel_return()
426
427def test_parallel_exceptions():
428    """
429    >>> test_parallel_exceptions()
430    I am executed first
431    ('propagate me',) 0
432    """
433    cdef int i, j, sum = 0
434
435    mylist = []
436
437    try:
438        for i in prange(10, nogil=True):
439            try:
440                for j in prange(10):
441                    with gil:
442                        raise Exception("propagate me")
443
444                    sum += i * j
445                sum += i
446            finally:
447                with gil:
448                    mylist.append("I am executed first")
449    except Exception, e:
450        print mylist[0]
451        print e.args, sum
452
453def test_parallel_exceptions_unnested():
454    """
455    >>> test_parallel_exceptions_unnested()
456    ('I am executed first', 0)
457    ('propagate me',) 0
458    """
459    cdef int i, sum = 0
460
461    mylist = []
462
463    try:
464        with nogil, cython.parallel.parallel():
465            try:
466                for i in prange(10):
467                    with gil:
468                        raise Exception("propagate me")
469
470                    sum += i
471            finally:
472                with gil:
473                    mylist.append(("I am executed first", sum))
474    except Exception, e:
475        print mylist[0]
476        print e.args, sum
477
478cdef int parallel_exc_cdef() except -3:
479    cdef int i, j
480    for i in prange(10, nogil=True):
481        for j in prange(10, num_threads=6):
482            with gil:
483                raise Exception("propagate me")
484
485    return 0
486
487cdef int parallel_exc_cdef_unnested() except -3:
488    cdef int i
489    for i in prange(10, nogil=True):
490        with gil:
491            raise Exception("propagate me")
492
493    return 0
494
495def test_parallel_exc_cdef():
496    """
497    >>> test_parallel_exc_cdef()
498    Traceback (most recent call last):
499        ...
500    Exception: propagate me
501    """
502    parallel_exc_cdef_unnested()
503    parallel_exc_cdef()
504
505cpdef int parallel_exc_cpdef() except -3:
506    cdef int i, j
507    for i in prange(10, nogil=True):
508        for j in prange(10, num_threads=6):
509            with gil:
510                raise Exception("propagate me")
511
512    return 0
513
514cpdef int parallel_exc_cpdef_unnested() except -3:
515    cdef int i, j
516    for i in prange(10, nogil=True):
517        with gil:
518            raise Exception("propagate me")
519
520    return 0
521
522
523def test_parallel_exc_cpdef():
524    """
525    >>> test_parallel_exc_cpdef()
526    Traceback (most recent call last):
527        ...
528    Exception: propagate me
529    """
530    parallel_exc_cpdef_unnested()
531    parallel_exc_cpdef()
532
533cdef int parallel_exc_nogil_swallow() except -1:
534    cdef int i, j
535    for i in prange(10, nogil=True):
536        try:
537            for j in prange(10):
538                with gil:
539                    raise Exception("propagate me")
540        finally:
541            return i
542
543    return 0
544
545cdef int parallel_exc_nogil_swallow_unnested() except -1:
546    cdef int i
547    with nogil:
548        try:
549            for i in prange(10):
550                with gil:
551                    raise Exception("propagate me")
552        finally:
553            return i
554
555    return 0
556
557def test_parallel_exc_nogil_swallow():
558    """
559    >>> test_parallel_exc_nogil_swallow()
560    execute me
561    execute me
562    """
563    parallel_exc_nogil_swallow_unnested()
564    print 'execute me'
565    parallel_exc_nogil_swallow()
566    print 'execute me'
567
568def parallel_exc_replace():
569    """
570    >>> parallel_exc_replace()
571    Traceback (most recent call last):
572        ...
573    Exception: propagate me instead
574    """
575    cdef int i, j
576    for i in prange(10, nogil=True):
577        with gil:
578            try:
579                for j in prange(10, nogil=True):
580                    with gil:
581                        raise Exception("propagate me")
582            except Exception, e:
583                raise Exception("propagate me instead")
584
585    return 0
586
587
588def parallel_exceptions2():
589    """
590    >>> parallel_exceptions2()
591    Traceback (most recent call last):
592       ...
593    Exception: propagate me
594    """
595    cdef int i, j, k
596
597    for i in prange(10, nogil=True):
598        for j in prange(10):
599            for k in prange(10):
600                if i + j + k > 20:
601                    with gil:
602                        raise Exception("propagate me")
603                        break
604                    continue
605                    return
606
607def test_parallel_with_gil_return():
608    """
609    >>> test_parallel_with_gil_return()
610    True
611    45
612    """
613    cdef int i, sum = 0
614
615    for i in prange(10, nogil=True):
616        with gil:
617            obj = i
618            sum += obj
619
620    print obj in range(10)
621
622    with nogil, cython.parallel.parallel():
623        with gil:
624            return sum
625
626def test_parallel_with_gil_continue_unnested():
627    """
628    >>> test_parallel_with_gil_continue_unnested()
629    20
630    """
631    cdef int i, sum = 0
632
633    for i in prange(10, nogil=True):
634        with gil:
635            if i % 2:
636                continue
637
638        sum += i
639
640    print sum
641
642
643cdef int inner_parallel_section() nogil:
644    cdef int j, sum = 0
645    for j in prange(10):
646        sum += j
647    return sum
648
649def outer_parallel_section():
650    """
651    >>> outer_parallel_section()
652    450
653    """
654    cdef int i, sum = 0
655    for i in prange(10, nogil=True):
656        sum += inner_parallel_section()
657    return sum
658
659cdef int nogil_cdef_except_clause() nogil except 0:
660    return 1
661
662cdef void nogil_cdef_except_star() nogil except *:
663    pass
664
665def test_nogil_cdef_except_clause():
666    """
667    >>> test_nogil_cdef_except_clause()
668    """
669    cdef int i
670    for i in prange(10, nogil=True):
671        nogil_cdef_except_clause()
672        nogil_cdef_except_star()
673
674def test_num_threads_compile():
675    cdef int i
676    for i in prange(10, nogil=True, num_threads=2):
677        pass
678
679    with nogil, cython.parallel.parallel(num_threads=2):
680        pass
681
682    with nogil, cython.parallel.parallel(num_threads=2):
683        for i in prange(10):
684            pass
685
686cdef int chunksize() nogil:
687    return 3
688
689def test_chunksize():
690    """
691    >>> test_chunksize()
692    45
693    45
694    45
695    """
696    cdef int i, sum
697
698    sum = 0
699    for i in prange(10, nogil=True, num_threads=2, schedule='static', chunksize=chunksize()):
700        sum += i
701    print sum
702
703    sum = 0
704    for i in prange(10, nogil=True, num_threads=6, schedule='dynamic', chunksize=chunksize()):
705        sum += i
706    print sum
707
708    sum = 0
709    with nogil, cython.parallel.parallel():
710        for i in prange(10, schedule='guided', chunksize=chunksize()):
711            sum += i
712    print sum
713
714
715cdef class PrintOnDealloc(object):
716    def __dealloc__(self):
717        print "deallocating..."
718
719def error():
720    raise Exception("propagate me")
721
722def test_clean_temps():
723    """
724    >>> test_clean_temps()
725    deallocating...
726    propagate me
727    """
728    cdef Py_ssize_t i
729
730    try:
731        for i in prange(100, nogil=True, num_threads=1):
732            with gil:
733                x = PrintOnDealloc() + error()
734    except Exception, e:
735        print e.args[0]
736
737
738def test_pointer_temps(double x):
739    """
740    >>> test_pointer_temps(1.0)
741    4.0
742    """
743    cdef Py_ssize_t i
744    cdef double* f
745    cdef double[:] arr = array(format="d", shape=(10,), itemsize=sizeof(double))
746    arr[0] = 4.0
747    arr[1] = 3.0
748
749    for i in prange(10, nogil=True, num_threads=1):
750        f = &arr[0]
751
752    return f[0]
753
754
755def test_prange_in_with(int x, ctx):
756    """
757    >>> from contextlib import contextmanager
758    >>> @contextmanager
759    ... def ctx(l): yield l
760    >>> test_prange_in_with(4, ctx([0]))
761    6
762    """
763    cdef int i
764    with ctx as l:
765        for i in prange(x, nogil=True):
766            with gil:
767                l[0] += i
768        return l[0]
769
770
771cdef extern from *:
772    """
773    #ifdef _OPENMP
774    #define _get_addr(_x, _idx) &_x
775    #else
776    #define _get_addr(_x, _idx) (&_x+_idx)
777    #endif
778    #define address_of_temp(store, temp, idx) store = _get_addr(temp, idx)
779    #define address_of_temp2(store, ignore, temp, idx) store = _get_addr(temp, idx)
780
781    double get_value() {
782        return 1.0;
783    }
784    """
785    void address_of_temp(...) nogil
786    void address_of_temp2(...) nogil
787    double get_value() nogil except -1.0  # will generate a temp for exception checking
788
789def test_inner_private():
790    """
791    Determines if a temp variable is private by taking its address in multiple threads
792    and seeing if they're the same (thread private variables should have different
793    addresses
794    >>> test_inner_private()
795    ok
796    """
797    cdef double* not_parallel[2]
798    cdef double* inner_vals[2]
799    cdef double* outer_vals[2]
800    cdef Py_ssize_t n, m
801
802    for n in range(2):
803        address_of_temp(not_parallel[n], get_value(), 0)
804    assert not_parallel[0] == not_parallel[1], "Addresses should be the same since they come from the same temp"
805
806    for n in prange(2, num_threads=2, schedule='static', chunksize=1, nogil=True):
807        address_of_temp(outer_vals[n], get_value(), n)
808        for m in prange(1):
809            # second temp just ensures different numbering
810            address_of_temp2(inner_vals[n], get_value(), get_value(), n)
811
812    inner_are_the_same = inner_vals[0] == inner_vals[1]
813    outer_are_the_same = outer_vals[0] == outer_vals[1]
814
815    assert outer_are_the_same == False, "Temporary variables in outer loop should be private"
816    assert inner_are_the_same == False,  "Temporary variables in inner loop should be private"
817
818    print('ok')
819