File Coverage

lib/Algorithm/EventsPerSecond/XS.xs
Criterion Covered Total %
statement 20 20 100.0
branch 8 8 100.0
condition n/a
subroutine n/a
pod n/a
total 28 28 100.0


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #include
7              
8             #if defined(__AVX2__) || defined(__SSE4_2__)
9             #include
10             #endif
11              
12             /* Fetch the raw int64_t buffer behind a packed Perl string. rw forces
13             the SV out of copy-on-write before we write into its buffer. */
14             static int64_t *
15 7246           aeps_buf(pTHX_ SV *sv, IV window, int rw)
16             {
17             STRLEN len;
18 7246 100         char *p = rw ? SvPV_force(sv, len) : SvPV(sv, len);
19 7246 100         if ((IV) len < window * (IV) sizeof(int64_t))
20 4           croak("Algorithm::EventsPerSecond: ring buffer smaller than window");
21 7242           return (int64_t *) p;
22             }
23              
24             static int64_t
25 2022           aeps_count(int64_t *buckets, int64_t *stamps, IV window, IV oldest)
26             {
27 2022           int64_t sum = 0;
28 2022           IV i = 0;
29              
30             #if defined(__AVX2__)
31             /* 4 buckets per iteration: stamps >= oldest becomes an all-ones
32             lane mask (>= via > oldest-1), which gates the bucket values
33             feeding a vector accumulator. */
34             {
35             __m256i vsum = _mm256_setzero_si256();
36             __m256i cutoff = _mm256_set1_epi64x((int64_t) oldest - 1);
37             int64_t lanes[4];
38             for (; i + 4 <= window; i += 4) {
39             __m256i s = _mm256_loadu_si256((__m256i const *) (stamps + i));
40             __m256i b = _mm256_loadu_si256((__m256i const *) (buckets + i));
41             __m256i keep = _mm256_cmpgt_epi64(s, cutoff);
42             vsum = _mm256_add_epi64(vsum, _mm256_and_si256(b, keep));
43             }
44             _mm256_storeu_si256((__m256i *) lanes, vsum);
45             sum = lanes[0] + lanes[1] + lanes[2] + lanes[3];
46             }
47             #elif defined(__SSE4_2__)
48             /* Same masked sum, 2 buckets per iteration. */
49             {
50             __m128i vsum = _mm_setzero_si128();
51             __m128i cutoff = _mm_set1_epi64x((int64_t) oldest - 1);
52             int64_t lanes[2];
53             for (; i + 2 <= window; i += 2) {
54             __m128i s = _mm_loadu_si128((__m128i const *) (stamps + i));
55             __m128i b = _mm_loadu_si128((__m128i const *) (buckets + i));
56             __m128i keep = _mm_cmpgt_epi64(s, cutoff);
57             vsum = _mm_add_epi64(vsum, _mm_and_si128(b, keep));
58             }
59             _mm_storeu_si128((__m128i *) lanes, vsum);
60             sum = lanes[0] + lanes[1];
61             }
62             #endif
63              
64             /* Tail (and whole loop when no intrinsics were compiled in);
65             branchless so -O3 can auto-vectorize it. */
66 284781 100         for (; i < window; i++)
67 282759           sum += buckets[i] & -(int64_t) (stamps[i] >= (int64_t) oldest);
68              
69 2022           return sum;
70             }
71              
72             MODULE = Algorithm::EventsPerSecond::XS PACKAGE = Algorithm::EventsPerSecond::XS
73              
74             PROTOTYPES: DISABLE
75              
76             void
77             _xs_mark(SV *buckets_sv, SV *stamps_sv, IV window, IV now, IV count)
78             CODE:
79             {
80 1600           int64_t *buckets = aeps_buf(aTHX_ buckets_sv, window, 1);
81 1599           int64_t *stamps = aeps_buf(aTHX_ stamps_sv, window, 1);
82 1598           IV i = now % window;
83              
84 1598 100         if (stamps[i] != (int64_t) now) {
85 1590           buckets[i] = 0;
86 1590           stamps[i] = (int64_t) now;
87             }
88 1598           buckets[i] += (int64_t) count;
89             }
90              
91             IV
92             _xs_count(SV *buckets_sv, SV *stamps_sv, IV window, IV oldest)
93             CODE:
94 2024           RETVAL = (IV) aeps_count(
95             aeps_buf(aTHX_ buckets_sv, window, 0),
96             aeps_buf(aTHX_ stamps_sv, window, 0),
97             window, oldest);
98             OUTPUT:
99             RETVAL
100              
101             const char *
102             _xs_simd()
103             CODE:
104             #if defined(__AVX2__)
105             RETVAL = "AVX2";
106             #elif defined(__SSE4_2__)
107             RETVAL = "SSE4.2";
108             #else
109 3           RETVAL = "scalar";
110             #endif
111             OUTPUT:
112             RETVAL