Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
06d69dd
Abstract out the call to array allocator as an intrinsic.
sklam May 24, 2021
81cba5d
Cleanup previous staging.
sklam May 25, 2021
c15fac1
Adding classmethod Array._allocate
sklam May 25, 2021
ac2708c
Use align instead of dtype
sklam May 25, 2021
ffcb989
Use array type to determine the classmethod
sklam May 25, 2021
b683418
Convert operator module functions to overloads.
stuartarchibald May 26, 2021
81ed617
Fix Black-Scholes test
stuartarchibald May 26, 2021
2b31732
Fix test_array_exprs
stuartarchibald May 26, 2021
64ca8bc
Flake8
stuartarchibald May 26, 2021
7ddd4d5
Fix error handling test
stuartarchibald May 26, 2021
37530ba
Fix debug test
stuartarchibald May 26, 2021
2cb8766
Fix up cache test
stuartarchibald May 26, 2021
2c0582c
Remove compile_isolated from svml tests
stuartarchibald May 27, 2021
6b8c62e
Fix test_debug in 32-bit platforms
sklam Jun 2, 2021
4188fe5
Fix fastmath propagation in SVML.
sklam Jun 3, 2021
b1b4fe3
Run SVML tests in isolated processes
sklam Jun 7, 2021
a5e7c43
Add inheriting behavior for error_model
sklam Jun 2, 2021
49f1dd8
Avoid compile_isolated in looplifting tests
sklam Jun 2, 2021
4024a9a
Revert "Convert operator module functions to overloads."
sklam Jun 8, 2021
57ebd93
Merge branch 'pr/7058' with revert of 0e323f5 into enh/arrayallocapi
sklam Jun 8, 2021
30b4eab
Revert patches specific to overload operators
sklam Jun 8, 2021
cec66fd
Test target specific overload_classmethod for allocator
stuartarchibald Jun 17, 2021
c5c08bb
Fix overload_method and attribute lookup not respecting hw class
sklam Jun 18, 2021
693f759
Fix typo
sklam Jun 23, 2021
609533a
Fix incorrect caching of BoundFunction due to target-overload
sklam Jun 23, 2021
1230e55
Merge branch 'master' into enh/arrayallocapi
sklam Jun 23, 2021
8fee312
Fix ir inlining test due to `inline` option in kwargs
sklam Jun 23, 2021
6c21b6d
More kwargs fixes
sklam Jun 24, 2021
144a840
Cleanup
sklam Jun 24, 2021
344c5b3
Apply suggestions from code review
sklam Jun 25, 2021
179767a
Remove dead code
sklam Jun 25, 2021
a5c48e6
Add timeout to avoid test hanging
sklam Jun 25, 2021
049923d
Remove redundant target_override and put nopython flag into jit_options
sklam Jun 25, 2021
f756fd0
PEP8
sklam Jun 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ exclude =
numba/tests/test_buffer_protocol.py
numba/tests/test_casting.py
numba/tests/test_lists.py
numba/tests/test_blackscholes.py
numba/tests/test_array_analysis.py
numba/tests/test_serialize.py
numba/tests/test_iteration.py
Expand Down
2 changes: 2 additions & 0 deletions numba/core/cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ def finalize(self, flags, options):

flags.inherit_if_not_set("fastmath")

flags.inherit_if_not_set("error_model", default="python")

# ----------------------------------------------------------------------------
# Internal

Expand Down
42 changes: 39 additions & 3 deletions numba/np/arrayobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from numba.cpython import slicing
from numba.cpython.unsafe.tuple import tuple_setitem, build_full_slice_tuple
from numba.core.overload_glue import glue_lowering
from numba.core.extending import overload_classmethod


def set_range_metadata(builder, load, lower_bound, upper_bound):
Expand Down Expand Up @@ -3475,10 +3476,16 @@ def _empty_nd_impl(context, builder, arrtype, shapes):
" the maximum possible size.",)
)

align = context.get_preferred_array_alignment(arrtype.dtype)
meminfo = context.nrt.meminfo_alloc_aligned(builder, size=allocsize,
align=align)
dtype = arrtype.dtype
align_val = context.get_preferred_array_alignment(dtype)
align = context.get_constant(types.uint32, align_val)
args = (context.get_dummy_value(), allocsize, align)

mip = types.MemInfoPointer(types.voidptr)
arytypeclass = types.TypeRef(type(arrtype))
argtypes = signature(mip, arytypeclass, types.intp, types.uint32)

meminfo = context.compile_internal(builder, _call_allocator, argtypes, args)
data = context.nrt.meminfo_data(builder, meminfo)

intp_t = context.get_value_type(types.intp)
Expand All @@ -3495,6 +3502,35 @@ def _empty_nd_impl(context, builder, arrtype, shapes):
return ary


@overload_classmethod(types.Array, "_allocate")
def _ol_array_allocate(cls, allocsize, align):
"""Implements a Numba-only classmethod on the array type.
Comment thread
sklam marked this conversation as resolved.
Resolved
"""
def impl(cls, allocsize, align):
return intrin_alloc(allocsize, align)
return impl


def _call_allocator(arrtype, size, align):
"""Trampoline to call intrinsic for allocation
Comment thread
sklam marked this conversation as resolved.
Resolved
"""
return arrtype._allocate(size, align)


@intrinsic
def intrin_alloc(typingctx, allocsize, align):
"""Intrinsic to call into the allocator for Array
"""
def codegen(context, builder, signature, args):
[allocsize, align] = args
meminfo = context.nrt.meminfo_alloc_aligned(builder, allocsize, align)
return meminfo

mip = types.MemInfoPointer(types.voidptr) # return untyped pointer
sig = signature(mip, allocsize, align)
return sig, codegen


def _zero_fill_array(context, builder, ary):
"""
Zero-fill an array. The array must be contiguous.
Expand Down
3 changes: 2 additions & 1 deletion numba/np/ufunc/array_exprs.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,10 @@ def _lower_array_expr(lowerer, expr):
inner_sig_args.append(argty)
inner_sig = outer_sig.return_type.dtype(*inner_sig_args)

flags = utils.ConfigStack().top_or_none() or compiler.Flags()
flags = flags.copy() # make sure it's a clone or a fresh instance
Comment thread
sklam marked this conversation as resolved.
Resolved
# Follow the Numpy error model. Note this also allows e.g. vectorizing
# division (issue #1223).
flags = compiler.Flags()
flags.error_model = 'numpy'
cres = context.compile_subroutine(builder, impl, inner_sig, flags=flags,
caching=False)
Expand Down
21 changes: 17 additions & 4 deletions numba/np/ufunc/ufuncbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,23 @@ def store_overloads_on_success():

# Use cache and compiler in a critical section
with global_compiler_lock:
with store_overloads_on_success():
# attempt look up of existing
cres = self.cache.load_overload(sig, targetctx)
if cres is not None:
with utils.ConfigStack().enter(flags.copy()):
with store_overloads_on_success():
# attempt look up of existing
cres = self.cache.load_overload(sig, targetctx)
if cres is not None:
return cres

# Compile
args, return_type = sigutils.normalize_signature(sig)
cres = compiler.compile_extra(typingctx, targetctx,
self.py_func, args=args,
return_type=return_type,
flags=flags, locals=locals)

# cache lookup failed before so safe to save
self.cache.save_overload(sig, cres)

Comment thread
stuartarchibald marked this conversation as resolved.
Resolved
return cres

# Compile
Expand Down
5 changes: 3 additions & 2 deletions numba/tests/test_array_exprs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from numba import typeof
from numba.core import utils, types, typing, ir, compiler, cpu, cgutils
from numba.core.compiler import Compiler, Flags
from numba.core.registry import cpu_target
from numba.tests.support import (MemoryLeakMixin, TestCase, temp_directory,
create_temp_module)
from numba.extending import (
Expand Down Expand Up @@ -95,9 +96,9 @@ def mk_pipeline(cls, args, return_type=None, flags=None, locals={},
flags = Flags()
flags.nrt = True
if typing_context is None:
typing_context = typing.Context()
typing_context = cpu_target.typing_context
if target_context is None:
target_context = cpu.CPUContext(typing_context)
target_context = cpu_target.target_context
return cls(typing_context, target_context, library, args, return_type,
flags, locals)

Expand Down
102 changes: 12 additions & 90 deletions numba/tests/test_blackscholes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import numpy as np

import unittest
from numba.core.compiler import compile_isolated, compile_extra, Flags
from numba.core import types, typing
from numba import njit
from numba.extending import register_jitable
from numba.tests.support import TestCase


Expand All @@ -20,13 +20,15 @@
RSQRT2PI = 0.39894228040143267793994605993438


@register_jitable
def cnd_array(d):
K = 1.0 / (1.0 + 0.2316419 * np.abs(d))
ret_val = (RSQRT2PI * np.exp(-0.5 * d * d) *
(K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5))))))
return np.where(d > 0, 1.0 - ret_val, ret_val)


@register_jitable
def cnd(d):
K = 1.0 / (1.0 + 0.2316419 * math.fabs(d))
ret_val = (RSQRT2PI * math.exp(-0.5 * d * d) *
Expand All @@ -36,6 +38,7 @@ def cnd(d):
return ret_val


@njit
def blackscholes_arrayexpr(stockPrice, optionStrike, optionYears, Riskfree,
Volatility):
S = stockPrice
Expand All @@ -56,28 +59,9 @@ def blackscholes_arrayexpr(stockPrice, optionStrike, optionYears, Riskfree,
return callResult, putResult


def blackscholes_arrayexpr_jitted(stockPrice, optionStrike, optionYears,
Riskfree, Volatility):
S = stockPrice
X = optionStrike
T = optionYears
R = Riskfree
V = Volatility
sqrtT = np.sqrt(T)
d1 = (np.log(S / X) + (R + 0.5 * V * V) * T) / (V * sqrtT)
d2 = d1 - V * sqrtT
cndd1 = cnd_array_jitted(d1)
cndd2 = cnd_array_jitted(d2)

expRT = np.exp(- R * T)

callResult = (S * cndd1 - X * expRT * cndd2)
putResult = (X * expRT * (1.0 - cndd2) - S * (1.0 - cndd1))
return callResult, putResult


@njit
def blackscholes_scalar(callResult, putResult, stockPrice, optionStrike,
optionYears, Riskfree, Volatility):
optionYears, Riskfree, Volatility):
S = stockPrice
X = optionStrike
T = optionYears
Expand All @@ -95,87 +79,28 @@ def blackscholes_scalar(callResult, putResult, stockPrice, optionStrike,
putResult[i] = (X[i] * expRT * (1.0 - cndd2) - S[i] * (1.0 - cndd1))


def blackscholes_scalar_jitted(callResult, putResult, stockPrice, optionStrike,
optionYears, Riskfree, Volatility):
S = stockPrice
X = optionStrike
T = optionYears
R = Riskfree
V = Volatility
for i in range(len(S)):
sqrtT = math.sqrt(T[i])
d1 = (math.log(S[i] / X[i]) + (R + 0.5 * V * V) * T[i]) / (V * sqrtT)
d2 = d1 - V * sqrtT
cndd1 = cnd_jitted(d1)
cndd2 = cnd_jitted(d2)

expRT = math.exp((-1. * R) * T[i])
callResult[i] = (S[i] * cndd1 - X[i] * expRT * cndd2)
putResult[i] = (X[i] * expRT * (1.0 - cndd2) - S[i] * (1.0 - cndd1))


def randfloat(rand_var, low, high):
return (1.0 - rand_var) * low + rand_var * high


class TestBlackScholes(TestCase):
def test_array_expr(self):
flags = Flags()
flags.enable_pyobject = True

global cnd_array_jitted
scalty = types.float64
arrty = types.Array(scalty, 1, 'C')
cr1 = compile_isolated(cnd_array, args=(arrty,), flags=flags)
cnd_array_jitted = cr1.entry_point
cr2 = compile_isolated(blackscholes_arrayexpr_jitted,
args=(arrty, arrty, arrty, scalty, scalty),
flags=flags)
jitted_bs = cr2.entry_point

OPT_N = 400
iterations = 10


stockPrice = randfloat(self.random.random_sample(OPT_N), 5.0, 30.0)
optionStrike = randfloat(self.random.random_sample(OPT_N), 1.0, 100.0)
optionYears = randfloat(self.random.random_sample(OPT_N), 0.25, 10.0)

args = stockPrice, optionStrike, optionYears, RISKFREE, VOLATILITY

callResultGold, putResultGold = blackscholes_arrayexpr(*args)
callResultNumba, putResultNumba = jitted_bs(*args)
callResultGold, putResultGold = blackscholes_arrayexpr.py_func(*args)
callResultNumba, putResultNumba = blackscholes_arrayexpr(*args)

delta = np.abs(callResultGold - callResultNumba)
L1norm = delta.sum() / np.abs(callResultGold).sum()
print("L1 norm: %E" % L1norm)
print("Max absolute error: %E" % delta.max())
self.assertEqual(delta.max(), 0)
self.assertAlmostEqual(delta.max(), 0)

def test_scalar(self):
flags = Flags()

# Compile the inner function
global cnd_jitted
cr1 = compile_isolated(cnd, (types.float64,))
cnd_jitted = cr1.entry_point
# Manually type the compiled function for calling into
tyctx = cr1.typing_context
ctx = cr1.target_context
signature = typing.make_concrete_template("cnd_jitted", cnd_jitted,
[cr1.signature])
tyctx.insert_user_function(cnd_jitted, signature)

# Compile the outer function
array = types.Array(types.float64, 1, 'C')
argtys = (array,) * 5 + (types.float64, types.float64)
cr2 = compile_extra(tyctx, ctx, blackscholes_scalar_jitted,
args=argtys, return_type=None, flags=flags,
locals={})
jitted_bs = cr2.entry_point

OPT_N = 400
iterations = 10

callResultGold = np.zeros(OPT_N)
putResultGold = np.zeros(OPT_N)
Expand All @@ -189,13 +114,10 @@ def test_scalar(self):

args = stockPrice, optionStrike, optionYears, RISKFREE, VOLATILITY

blackscholes_scalar(callResultGold, putResultGold, *args)
jitted_bs(callResultNumba, putResultNumba, *args)
blackscholes_scalar.py_func(callResultGold, putResultGold, *args)
blackscholes_scalar(callResultNumba, putResultNumba, *args)

delta = np.abs(callResultGold - callResultNumba)
L1norm = delta.sum() / np.abs(callResultGold).sum()
print("L1 norm: %E" % L1norm)
print("Max absolute error: %E" % delta.max())
self.assertAlmostEqual(delta.max(), 0)


Expand Down
Loading