Skip to content

Commit f7f1df0

Browse files
przembcharris
authored andcommitted
MAINT: Large overhead in some random functions numpy#15460
slow calls to np.dtype.name replaced with np.dtype, mtrand.pyx and _generator.pyx updated, test test_warns_byteorder updated before: %timeit rs.random(): 520 ns ± 33.1 ns per loop %timeit rg.random(): 6.36 µs ± 222 ns per loop after: %timeit rs.random(): 453 ns ± 6.95 ns per loop %timeit rg.random(): 594 ns ± 9.66 ns per loop
1 parent e0660d4 commit f7f1df0

File tree

3 files changed

+53
-59
lines changed

3 files changed

+53
-59
lines changed

numpy/random/_generator.pyx

+35-36
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ from libc.stdint cimport (uint8_t, uint16_t, uint32_t, uint64_t,
1717
from ._bounded_integers cimport (_rand_bool, _rand_int32, _rand_int64,
1818
_rand_int16, _rand_int8, _rand_uint64, _rand_uint32, _rand_uint16,
1919
_rand_uint8, _gen_mask)
20-
from ._bounded_integers import _integers_types
2120
from ._pcg64 import PCG64
2221
from numpy.random cimport bitgen_t
2322
from ._common cimport (POISSON_LAM_MAX, CONS_POSITIVE, CONS_NONE,
@@ -312,13 +311,13 @@ cdef class Generator:
312311
313312
"""
314313
cdef double temp
315-
key = np.dtype(dtype).name
316-
if key == 'float64':
314+
_dtype = np.dtype(dtype)
315+
if _dtype == np.float64:
317316
return double_fill(&random_standard_uniform_fill, &self._bitgen, size, self.lock, out)
318-
elif key == 'float32':
317+
elif _dtype == np.float32:
319318
return float_fill(&random_standard_uniform_fill_f, &self._bitgen, size, self.lock, out)
320319
else:
321-
raise TypeError('Unsupported dtype "%s" for random' % key)
320+
raise TypeError('Unsupported dtype %r for random' % _dtype)
322321

323322
def beta(self, a, b, size=None):
324323
"""
@@ -454,20 +453,20 @@ cdef class Generator:
454453
>>> n = np.random.default_rng().standard_exponential((3, 8000))
455454
456455
"""
457-
key = np.dtype(dtype).name
458-
if key == 'float64':
456+
_dtype = np.dtype(dtype)
457+
if _dtype == np.float64:
459458
if method == u'zig':
460459
return double_fill(&random_standard_exponential_fill, &self._bitgen, size, self.lock, out)
461460
else:
462461
return double_fill(&random_standard_exponential_inv_fill, &self._bitgen, size, self.lock, out)
463-
elif key == 'float32':
462+
elif _dtype == np.float32:
464463
if method == u'zig':
465464
return float_fill(&random_standard_exponential_fill_f, &self._bitgen, size, self.lock, out)
466465
else:
467466
return float_fill(&random_standard_exponential_inv_fill_f, &self._bitgen, size, self.lock, out)
468467
else:
469-
raise TypeError('Unsupported dtype "%s" for standard_exponential'
470-
% key)
468+
raise TypeError('Unsupported dtype %r for standard_exponential'
469+
% _dtype)
471470

472471
def integers(self, low, high=None, size=None, dtype=np.int64, endpoint=False):
473472
"""
@@ -559,39 +558,39 @@ cdef class Generator:
559558
high = low
560559
low = 0
561560

562-
dt = np.dtype(dtype)
563-
key = dt.name
564-
if key not in _integers_types:
565-
raise TypeError('Unsupported dtype "%s" for integers' % key)
566-
if not dt.isnative:
567-
raise ValueError('Providing a dtype with a non-native byteorder '
568-
'is not supported. If you require '
569-
'platform-independent byteorder, call byteswap '
570-
'when required.')
561+
_dtype = np.dtype(dtype)
571562

572563
# Implementation detail: the old API used a masked method to generate
573564
# bounded uniform integers. Lemire's method is preferable since it is
574565
# faster. randomgen allows a choice, we will always use the faster one.
575566
cdef bint _masked = False
576567

577-
if key == 'int32':
568+
if _dtype == np.int32:
578569
ret = _rand_int32(low, high, size, _masked, endpoint, &self._bitgen, self.lock)
579-
elif key == 'int64':
570+
elif _dtype == np.int64:
580571
ret = _rand_int64(low, high, size, _masked, endpoint, &self._bitgen, self.lock)
581-
elif key == 'int16':
572+
elif _dtype == np.int16:
582573
ret = _rand_int16(low, high, size, _masked, endpoint, &self._bitgen, self.lock)
583-
elif key == 'int8':
574+
elif _dtype == np.int8:
584575
ret = _rand_int8(low, high, size, _masked, endpoint, &self._bitgen, self.lock)
585-
elif key == 'uint64':
576+
elif _dtype == np.uint64:
586577
ret = _rand_uint64(low, high, size, _masked, endpoint, &self._bitgen, self.lock)
587-
elif key == 'uint32':
578+
elif _dtype == np.uint32:
588579
ret = _rand_uint32(low, high, size, _masked, endpoint, &self._bitgen, self.lock)
589-
elif key == 'uint16':
580+
elif _dtype == np.uint16:
590581
ret = _rand_uint16(low, high, size, _masked, endpoint, &self._bitgen, self.lock)
591-
elif key == 'uint8':
582+
elif _dtype == np.uint8:
592583
ret = _rand_uint8(low, high, size, _masked, endpoint, &self._bitgen, self.lock)
593-
elif key == 'bool':
584+
elif _dtype == np.bool_:
594585
ret = _rand_bool(low, high, size, _masked, endpoint, &self._bitgen, self.lock)
586+
elif not _dtype.isnative:
587+
raise ValueError('Providing a dtype with a non-native byteorder '
588+
'is not supported. If you require '
589+
'platform-independent byteorder, call byteswap '
590+
'when required.')
591+
else:
592+
raise TypeError('Unsupported dtype %r for integers' % _dtype)
593+
595594

596595
if size is None and dtype in (bool, int, np.compat.long):
597596
if np.array(ret).shape == ():
@@ -1038,14 +1037,14 @@ cdef class Generator:
10381037
[ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) # random
10391038
10401039
"""
1041-
key = np.dtype(dtype).name
1042-
if key == 'float64':
1040+
_dtype = np.dtype(dtype)
1041+
if _dtype == np.float64:
10431042
return double_fill(&random_standard_normal_fill, &self._bitgen, size, self.lock, out)
1044-
elif key == 'float32':
1043+
elif _dtype == np.float32:
10451044
return float_fill(&random_standard_normal_fill_f, &self._bitgen, size, self.lock, out)
10461045

10471046
else:
1048-
raise TypeError('Unsupported dtype "%s" for standard_normal' % key)
1047+
raise TypeError('Unsupported dtype %r for standard_normal' % _dtype)
10491048

10501049
def normal(self, loc=0.0, scale=1.0, size=None):
10511050
"""
@@ -1227,19 +1226,19 @@ cdef class Generator:
12271226
12281227
"""
12291228
cdef void *func
1230-
key = np.dtype(dtype).name
1231-
if key == 'float64':
1229+
_dtype = np.dtype(dtype)
1230+
if _dtype == np.float64:
12321231
return cont(&random_standard_gamma, &self._bitgen, size, self.lock, 1,
12331232
shape, 'shape', CONS_NON_NEGATIVE,
12341233
0.0, '', CONS_NONE,
12351234
0.0, '', CONS_NONE,
12361235
out)
1237-
if key == 'float32':
1236+
if _dtype == np.float32:
12381237
return cont_f(&random_standard_gamma_f, &self._bitgen, size, self.lock,
12391238
shape, 'shape', CONS_NON_NEGATIVE,
12401239
out)
12411240
else:
1242-
raise TypeError('Unsupported dtype "%s" for standard_gamma' % key)
1241+
raise TypeError('Unsupported dtype %r for standard_gamma' % _dtype)
12431242

12441243
def gamma(self, shape, scale=1.0, size=None):
12451244
"""

numpy/random/mtrand.pyx

+17-21
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ from libc.stdint cimport int64_t, uint64_t
1515
from ._bounded_integers cimport (_rand_bool, _rand_int32, _rand_int64,
1616
_rand_int16, _rand_int8, _rand_uint64, _rand_uint32, _rand_uint16,
1717
_rand_uint8,)
18-
from ._bounded_integers import _integers_types
1918
from ._mt19937 import MT19937 as _MT19937
2019
from numpy.random cimport bitgen_t
2120
from ._common cimport (POISSON_LAM_MAX, CONS_POSITIVE, CONS_NONE,
@@ -722,17 +721,7 @@ cdef class RandomState:
722721
high = low
723722
low = 0
724723

725-
dt = np.dtype(dtype)
726-
key = dt.name
727-
if key not in _integers_types:
728-
raise TypeError('Unsupported dtype "%s" for randint' % key)
729-
if not dt.isnative:
730-
# numpy 1.17.0, 2019-05-28
731-
warnings.warn('Providing a dtype with a non-native byteorder is '
732-
'not supported. If you require platform-independent '
733-
'byteorder, call byteswap when required.\nIn future '
734-
'version, providing byteorder will raise a '
735-
'ValueError', DeprecationWarning)
724+
_dtype = np.dtype(dtype)
736725

737726
# Implementation detail: the use a masked method to generate
738727
# bounded uniform integers. Lemire's method is preferable since it is
@@ -741,24 +730,31 @@ cdef class RandomState:
741730
cdef bint _masked = True
742731
cdef bint _endpoint = False
743732

744-
if key == 'int32':
733+
if _dtype == np.int32:
745734
ret = _rand_int32(low, high, size, _masked, _endpoint, &self._bitgen, self.lock)
746-
elif key == 'int64':
735+
elif _dtype == np.int64:
747736
ret = _rand_int64(low, high, size, _masked, _endpoint, &self._bitgen, self.lock)
748-
elif key == 'int16':
737+
elif _dtype == np.int16:
749738
ret = _rand_int16(low, high, size, _masked, _endpoint, &self._bitgen, self.lock)
750-
elif key == 'int8':
739+
elif _dtype == np.int8:
751740
ret = _rand_int8(low, high, size, _masked, _endpoint, &self._bitgen, self.lock)
752-
elif key == 'uint64':
741+
elif _dtype == np.uint64:
753742
ret = _rand_uint64(low, high, size, _masked, _endpoint, &self._bitgen, self.lock)
754-
elif key == 'uint32':
743+
elif _dtype == np.uint32:
755744
ret = _rand_uint32(low, high, size, _masked, _endpoint, &self._bitgen, self.lock)
756-
elif key == 'uint16':
745+
elif _dtype == np.uint16:
757746
ret = _rand_uint16(low, high, size, _masked, _endpoint, &self._bitgen, self.lock)
758-
elif key == 'uint8':
747+
elif _dtype == np.uint8:
759748
ret = _rand_uint8(low, high, size, _masked, _endpoint, &self._bitgen, self.lock)
760-
elif key == 'bool':
749+
elif _dtype == np.bool_:
761750
ret = _rand_bool(low, high, size, _masked, _endpoint, &self._bitgen, self.lock)
751+
elif not _dtype.isnative:
752+
raise ValueError('Providing a dtype with a non-native byteorder '
753+
'is not supported. If you require '
754+
'platform-independent byteorder, call byteswap '
755+
'when required.')
756+
else:
757+
raise TypeError('Unsupported dtype %r for randint' % _dtype)
762758

763759
if size is None and dtype in (bool, int, np.compat.long):
764760
if np.array(ret).shape == ():

numpy/random/tests/test_randomstate_regression.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ def __array__(self):
162162
def test_warns_byteorder(self):
163163
# GH 13159
164164
other_byteord_dt = '<i4' if sys.byteorder == 'big' else '>i4'
165-
with pytest.deprecated_call(match='non-native byteorder is not'):
166-
random.randint(0, 200, size=10, dtype=other_byteord_dt)
165+
assert_raises(ValueError, random.randint, 0, 200, size=10, dtype=other_byteord_dt)
167166

168167
def test_named_argument_initialization(self):
169168
# GH 13669

0 commit comments

Comments
 (0)