-
Notifications
You must be signed in to change notification settings - Fork 16
Fix integer fills and bytes() for BRNGs without viRngUniformBits support
#175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
vlad-perevezentsev
wants to merge
11
commits into
master
Choose a base branch
from
fix-scalar-full-range-brng
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
e3fdbe8
Support all BRNGs in scalar full-range randint via viRngUniform fallback
vlad-perevezentsev 78e02ed
Unskip scalar full-range randint test for all BRNGs
vlad-perevezentsev 684cf03
Support all BRNGs in legacy long randint fills via viRngUniform fallback
vlad-perevezentsev 37dc783
Add test_legacy_long_path_full_range
vlad-perevezentsev c5c561b
Merge remote-tracking branch 'origin/master' into fix-scalar-full-ran…
vlad-perevezentsev fb9aa86
Fix 64-bit integer fills of 2**30 elements or more
vlad-perevezentsev 6ad2551
Support all BRNGs in bytes via viRngUniform fallback
vlad-perevezentsev 9c64850
Add test_bytes_all_brngs to test_random.py
vlad-perevezentsev 5c87c38
Add gh-175 to changelog
vlad-perevezentsev bab16e8
Remove randint_untyped from the legacy long-path test
vlad-perevezentsev b89c330
Merge branch 'master' into fix-scalar-full-range-brng
ndgrigorian File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1620,6 +1620,103 @@ void irk_discrete_uniform_vec(irk_state *state, | |
| assert(err == VSL_STATUS_OK); | ||
| } | ||
|
|
||
| /* | ||
| * Bulk source of raw uniform words for the bounded-integer | ||
| * routines below, overloaded on the word type (32/64-bit). BRNGs that lack | ||
| * viRngUniformBits fall back to assembling words from viRngUniform. | ||
| */ | ||
| static inline void | ||
| irk_uniform_bits_vec(irk_state *state, npy_intp len, npy_uint32 *buf) | ||
| { | ||
| int err = 0; | ||
| npy_intp i = 0; | ||
|
|
||
| while (len > 0) { | ||
| MKL_INT c = (len > MKL_INT_MAX) ? (MKL_INT)MKL_INT_MAX : (MKL_INT)len; | ||
| err = viRngUniformBits32(VSL_RNG_METHOD_UNIFORMBITS32_STD, | ||
| state->stream, c, (unsigned int *)buf); | ||
| if (err == VSL_RNG_ERROR_BRNG_NOT_SUPPORTED) { | ||
| /* viRngUniformBits32 unsupported for WH/MCG31/R250/MRG32K3A; | ||
| * build each word from two 16-bit viRngUniform halves */ | ||
| npy_intp total = 2 * (npy_intp)c, rem = total, off = 0; | ||
| int *tmp = (int *)mkl_malloc(total * sizeof(int), 64); | ||
| assert(tmp != nullptr); | ||
| /* one call unless the count exceeds MKL_INT */ | ||
| while (rem > 0) { | ||
| MKL_INT cc = | ||
| (rem > MKL_INT_MAX) ? (MKL_INT)MKL_INT_MAX : (MKL_INT)rem; | ||
| err = viRngUniform(VSL_RNG_METHOD_UNIFORM_STD, state->stream, | ||
| cc, tmp + off, 0, 65536); | ||
| assert(err == VSL_STATUS_OK); | ||
| off += cc; | ||
| rem -= cc; | ||
| } | ||
| for (i = 0; i < c; ++i) | ||
| buf[i] = ((npy_uint32)tmp[2 * i]) | | ||
| (((npy_uint32)tmp[2 * i + 1]) << 16); | ||
| mkl_free(tmp); | ||
| } | ||
| else { | ||
| assert(err == VSL_STATUS_OK); | ||
| } | ||
| buf += c; | ||
| len -= c; | ||
| } | ||
| } | ||
|
|
||
| static inline void | ||
| irk_uniform_bits_vec(irk_state *state, npy_intp len, npy_uint64 *buf) | ||
| { | ||
| int err = 0; | ||
| npy_intp i = 0; | ||
| /* viRngUniformBits64 counts 32-bit words, so its count must fit half of | ||
| * MKL_INT; larger requests under-fill the buffer or crash */ | ||
| const npy_intp bits64_max = MKL_INT_MAX / 2; | ||
|
|
||
| while (len > 0) { | ||
| MKL_INT c = (len > bits64_max) ? (MKL_INT)bits64_max : (MKL_INT)len; | ||
| err = viRngUniformBits64(VSL_RNG_METHOD_UNIFORMBITS64_STD, | ||
| state->stream, c, (unsigned MKL_INT64 *)buf); | ||
| if (err == VSL_RNG_ERROR_BRNG_NOT_SUPPORTED) { | ||
| /* viRngUniformBits64 unsupported for WH/MCG31/R250/MRG32K3A; | ||
| * build each word from four 16-bit viRngUniform halves */ | ||
| npy_intp total = 4 * (npy_intp)c, rem = total, off = 0; | ||
| int *tmp = (int *)mkl_malloc(total * sizeof(int), 64); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same point here |
||
| assert(tmp != nullptr); | ||
| /* one call unless the count exceeds MKL_INT */ | ||
| while (rem > 0) { | ||
| MKL_INT cc = | ||
| (rem > MKL_INT_MAX) ? (MKL_INT)MKL_INT_MAX : (MKL_INT)rem; | ||
| err = viRngUniform(VSL_RNG_METHOD_UNIFORM_STD, state->stream, | ||
| cc, tmp + off, 0, 65536); | ||
| assert(err == VSL_STATUS_OK); | ||
| off += cc; | ||
| rem -= cc; | ||
| } | ||
| for (i = 0; i < c; ++i) | ||
| buf[i] = ((npy_uint64)(npy_uint32)tmp[4 * i]) | | ||
| (((npy_uint64)(npy_uint32)tmp[4 * i + 1]) << 16) | | ||
| (((npy_uint64)(npy_uint32)tmp[4 * i + 2]) << 32) | | ||
| (((npy_uint64)(npy_uint32)tmp[4 * i + 3]) << 48); | ||
| mkl_free(tmp); | ||
| } | ||
| else { | ||
| assert(err == VSL_STATUS_OK); | ||
| } | ||
| buf += c; | ||
| len -= c; | ||
| } | ||
| } | ||
|
|
||
| /* C-linkage entry point: other files cannot call the overloads above. */ | ||
| void irk_uniform_bits32_vec(irk_state *state, npy_intp len, npy_uint32 *res) | ||
| { | ||
| if (len < 1) | ||
| return; | ||
|
|
||
| irk_uniform_bits_vec(state, len, res); | ||
| } | ||
|
|
||
| void irk_discrete_uniform_long_vec(irk_state *state, | ||
| npy_intp len, | ||
| long *res, | ||
|
|
@@ -1685,9 +1782,7 @@ void irk_discrete_uniform_long_vec(irk_state *state, | |
| while (n_accepted < len) { | ||
| int k, batchSize = len - n_accepted; | ||
|
|
||
| err = viRngUniformBits64(VSL_RNG_METHOD_UNIFORM_STD, state->stream, | ||
| batchSize, (unsigned MKL_INT64 *)buf); | ||
| assert(err == VSL_STATUS_OK); | ||
| irk_uniform_bits_vec(state, batchSize, (npy_uint64 *)buf); | ||
|
|
||
| for (k = 0; k < batchSize; ++k) { | ||
| unsigned long value = buf[k] & mask; | ||
|
|
@@ -1703,8 +1798,6 @@ void irk_discrete_uniform_long_vec(irk_state *state, | |
|
|
||
| void irk_ulong_vec(irk_state *state, npy_intp len, unsigned long *res) | ||
| { | ||
| int err = 0; | ||
|
|
||
| if (len < 1) | ||
| return; | ||
|
|
||
|
|
@@ -1716,14 +1809,10 @@ void irk_ulong_vec(irk_state *state, npy_intp len, unsigned long *res) | |
| } | ||
|
|
||
| #if ULONG_MAX <= 0xffffffffUL | ||
| err = viRngUniformBits32(VSL_RNG_METHOD_UNIFORMBITS32_STD, state->stream, | ||
| len, (unsigned int *)res); | ||
| irk_uniform_bits_vec(state, len, (npy_uint32 *)res); | ||
| #else | ||
| err = viRngUniformBits64(VSL_RNG_METHOD_UNIFORMBITS64_STD, state->stream, | ||
| len, (unsigned MKL_INT64 *)res); | ||
| irk_uniform_bits_vec(state, len, (npy_uint64 *)res); | ||
| #endif | ||
|
|
||
| assert(err == VSL_STATUS_OK); | ||
| } | ||
|
|
||
| void irk_long_vec(irk_state *state, npy_intp len, long *res) | ||
|
|
@@ -1844,9 +1933,7 @@ void irk_rand_uint32_vec(irk_state *state, | |
|
|
||
| /* optimization for lo = 0 and hi = 2**32-1 */ | ||
| if (!(lo || ~hi)) { | ||
| err = viRngUniformBits32(VSL_RNG_METHOD_UNIFORMBITS32_STD, | ||
| state->stream, len, (unsigned int *)res); | ||
| assert(err == VSL_STATUS_OK); | ||
| irk_uniform_bits_vec(state, len, res); | ||
|
|
||
| return; | ||
| } | ||
|
|
@@ -1935,9 +2022,7 @@ void irk_rand_uint64_vec(irk_state *state, | |
|
|
||
| /* optimization for lo = 0 and hi = 2**64-1 */ | ||
| if (!(lo || ~hi)) { | ||
| err = viRngUniformBits64(VSL_RNG_METHOD_UNIFORMBITS64_STD, | ||
| state->stream, len, (unsigned MKL_INT64 *)res); | ||
| assert(err == VSL_STATUS_OK); | ||
| irk_uniform_bits_vec(state, len, res); | ||
|
|
||
| return; | ||
| } | ||
|
|
@@ -1983,10 +2068,7 @@ void irk_rand_uint64_vec(irk_state *state, | |
| if (mask == rng) { | ||
| /* rng + 1 is a power of two, so masking alone confines every draw | ||
| * to [0, rng] and nothing is rejected. Fill res directly. */ | ||
| err = viRngUniformBits64(VSL_RNG_METHOD_UNIFORMBITS64_STD, | ||
| state->stream, len, | ||
| (unsigned MKL_INT64 *)res); | ||
| assert(err == VSL_STATUS_OK); | ||
| irk_uniform_bits_vec(state, len, res); | ||
|
|
||
| DIST_PRAGMA_VECTOR | ||
| for (i = 0; i < len; ++i) | ||
|
|
@@ -1997,9 +2079,7 @@ void irk_rand_uint64_vec(irk_state *state, | |
|
|
||
| /* Draw into res and compact in place; n_accepted never runs ahead of i, | ||
| * so the store cannot clobber an unread value. Acceptance is > 1/2. */ | ||
| err = viRngUniformBits64(VSL_RNG_METHOD_UNIFORMBITS64_STD, | ||
| state->stream, len, (unsigned MKL_INT64 *)res); | ||
| assert(err == VSL_STATUS_OK); | ||
| irk_uniform_bits_vec(state, len, res); | ||
|
|
||
| for (i = 0; i < len; ++i) { | ||
| npy_uint64 value = res[i] & mask; | ||
|
|
@@ -2017,10 +2097,7 @@ void irk_rand_uint64_vec(irk_state *state, | |
| npy_intp k = 0; | ||
| npy_intp batchSize = len - n_accepted; | ||
|
|
||
| err = viRngUniformBits64(VSL_RNG_METHOD_UNIFORMBITS64_STD, | ||
| state->stream, batchSize, | ||
| (unsigned MKL_INT64 *)buf); | ||
| assert(err == VSL_STATUS_OK); | ||
| irk_uniform_bits_vec(state, batchSize, buf); | ||
|
|
||
| for (k = 0; k < batchSize; ++k) { | ||
| npy_uint64 value = buf[k] & mask; | ||
|
|
@@ -2056,91 +2133,6 @@ void irk_rand_int64_vec(irk_state *state, | |
| res[i] = res[i] + lo; | ||
| } | ||
|
|
||
| /* | ||
| * Bulk source of raw uniform words for the broadcasted bounded-integer | ||
| * routines below, overloaded on the word type (32/64-bit). BRNGs that lack | ||
| * viRngUniformBits fall back to assembling words from viRngUniform. | ||
| */ | ||
| static inline void | ||
| irk_uniform_bits_vec(irk_state *state, npy_intp len, npy_uint32 *buf) | ||
| { | ||
| int err = 0; | ||
| npy_intp i = 0; | ||
|
|
||
| while (len > 0) { | ||
| MKL_INT c = (len > MKL_INT_MAX) ? (MKL_INT)MKL_INT_MAX : (MKL_INT)len; | ||
| err = viRngUniformBits32(VSL_RNG_METHOD_UNIFORMBITS32_STD, | ||
| state->stream, c, (unsigned int *)buf); | ||
| if (err == VSL_RNG_ERROR_BRNG_NOT_SUPPORTED) { | ||
| /* viRngUniformBits32 unsupported for WH/MCG31/R250/MRG32K3A; | ||
| * build each word from two 16-bit viRngUniform halves */ | ||
| npy_intp total = 2 * (npy_intp)c, rem = total, off = 0; | ||
| int *tmp = (int *)mkl_malloc(total * sizeof(int), 64); | ||
| assert(tmp != nullptr); | ||
| /* one call unless the count exceeds MKL_INT */ | ||
| while (rem > 0) { | ||
| MKL_INT cc = | ||
| (rem > MKL_INT_MAX) ? (MKL_INT)MKL_INT_MAX : (MKL_INT)rem; | ||
| err = viRngUniform(VSL_RNG_METHOD_UNIFORM_STD, state->stream, | ||
| cc, tmp + off, 0, 65536); | ||
| assert(err == VSL_STATUS_OK); | ||
| off += cc; | ||
| rem -= cc; | ||
| } | ||
| for (i = 0; i < c; ++i) | ||
| buf[i] = ((npy_uint32)tmp[2 * i]) | | ||
| (((npy_uint32)tmp[2 * i + 1]) << 16); | ||
| mkl_free(tmp); | ||
| } | ||
| else { | ||
| assert(err == VSL_STATUS_OK); | ||
| } | ||
| buf += c; | ||
| len -= c; | ||
| } | ||
| } | ||
|
|
||
| static inline void | ||
| irk_uniform_bits_vec(irk_state *state, npy_intp len, npy_uint64 *buf) | ||
| { | ||
| int err = 0; | ||
| npy_intp i = 0; | ||
|
|
||
| while (len > 0) { | ||
| MKL_INT c = (len > MKL_INT_MAX) ? (MKL_INT)MKL_INT_MAX : (MKL_INT)len; | ||
| err = viRngUniformBits64(VSL_RNG_METHOD_UNIFORMBITS64_STD, | ||
| state->stream, c, (unsigned MKL_INT64 *)buf); | ||
| if (err == VSL_RNG_ERROR_BRNG_NOT_SUPPORTED) { | ||
| /* viRngUniformBits64 unsupported for WH/MCG31/R250/MRG32K3A; | ||
| * build each word from four 16-bit viRngUniform halves */ | ||
| npy_intp total = 4 * (npy_intp)c, rem = total, off = 0; | ||
| int *tmp = (int *)mkl_malloc(total * sizeof(int), 64); | ||
| assert(tmp != nullptr); | ||
| /* one call unless the count exceeds MKL_INT */ | ||
| while (rem > 0) { | ||
| MKL_INT cc = | ||
| (rem > MKL_INT_MAX) ? (MKL_INT)MKL_INT_MAX : (MKL_INT)rem; | ||
| err = viRngUniform(VSL_RNG_METHOD_UNIFORM_STD, state->stream, | ||
| cc, tmp + off, 0, 65536); | ||
| assert(err == VSL_STATUS_OK); | ||
| off += cc; | ||
| rem -= cc; | ||
| } | ||
| for (i = 0; i < c; ++i) | ||
| buf[i] = ((npy_uint64)(npy_uint32)tmp[4 * i]) | | ||
| (((npy_uint64)(npy_uint32)tmp[4 * i + 1]) << 16) | | ||
| (((npy_uint64)(npy_uint32)tmp[4 * i + 2]) << 32) | | ||
| (((npy_uint64)(npy_uint32)tmp[4 * i + 3]) << 48); | ||
| mkl_free(tmp); | ||
| } | ||
| else { | ||
| assert(err == VSL_STATUS_OK); | ||
| } | ||
| buf += c; | ||
| len -= c; | ||
| } | ||
| } | ||
|
|
||
| /* mulhi for Lemire (word * s): top 32 bits of the product, low to *lo. */ | ||
| static inline npy_uint32 irk_mulhi(npy_uint32 a, npy_uint32 b, npy_uint32 *lo) | ||
| { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This buffer is two times the size of the original length, so we end up spending a potentially huge amount of memory here, and we have no fallback for the case where the allocation fails either.
We could instead have a buffer that we reuse that's smaller and fill in chunks