Memory Bug, Possible Undefined Baheviour
Create the following unit test file, which is based on program_strides_2() from tests/numpy/array_creation_test.py.
import dace
import numpy as np
import copy
@dace.program
def program():
# Was originally `program_strides_2()` in `tests/numpy/array_creation_test.py`.
A = dace.ndarray((2, 2), dtype=dace.int32, strides=(1, 2))
for i, j in dace.map[0:2, 0:2]:
A[i, j] = j + i
return A
def _impl_of_memory_test():
sdfg = program.to_sdfg()
csdfg = sdfg.compile()
A1 = csdfg()
Res1 = copy.deepcopy(A1)
A2 = csdfg()
assert A1 is A2
Res2 = copy.deepcopy(A2)
assert A2.strides == (4, 8)
assert np.allclose(Res2, [[0, 1], [1, 2]]), "Never expected that this fails."
assert np.allclose(Res1, [[0, 1], [1, 2]]), "Expected that this fails."
def test():
_impl_of_memory_test()
if __name__ == "__main__":
test()
Note that the bug does not surfaces every time, so you have to call the test in the following way:
for i in $(seq 100) ; do echo "ITERATION ${i}" ; pytest memory_issue_test.py --pdb ; done
It usually happens within the first 10 to 20 iterations.
I tried the following:
- If the Maps are serial, then it works as expected.
- If we add an additional operation, i.e. at the end we add
A[0, 0] += 1the error still surfaces. - If we set
may_aliasof__returntoTrueit still fails. - if we change the assignment to any of
A[i, j] = 0,A[i, j] = iorA[i, j] = jthen the test passes. - If we move the for loop that calls the test, i.e. the bash loop suggested above, into python, then it seems that the bug is not triggered.
- Passing
Aas an argument, i.e. allocating it explicitly, the result is still the same, it fails; See below for the code. - I do not observe the issue if
Ais allocated inCorder (for that I used the version that passesAas argument).
I tried it on commit 8c24a345277ecf5d0e8f9a9c3f85a2630c367a9a, which was main at the point.
I am using Python 3.9.20 and 3.12.3 (but I have the impression that it happens less often for it).
Here is a second reproducer, that allocates the array outside.
- If the memory order of
Ais switched fromFtoCthen the code works as expected (did not crash within 100 invocations). - If
Ais not initialized withnp.emptybut withnp.zerosit works as expected (this is the same behaviour I see; Only the second write takes effect).
import dace
import numpy as np
import copy
# Setting `order` to `C` will stop the issues.
order = "F"
if order == "F":
AType = dace.data.Array(dace.int32, shape=(2, 2), strides=(1, 2))
else:
assert order == "C"
AType = dace.data.Array(dace.int32, shape=(2, 2))
# Was originally `program_strides_2()` in `tests/numpy/array_creation_test.py`.
@dace.program
def program(A: AType):
for i, j in dace.map[0:2, 0:2]:
A[i, j] = j + i
def _impl_of_memory_test():
sdfg = program.to_sdfg()
csdfg = sdfg.compile()
A = np.empty((2, 2), dtype=np.int32, order=order)
if order == "F":
assert A.strides == (4, 8)
else:
assert A.strides == (8, 4)
csdfg(A)
Res1 = copy.deepcopy(A)
csdfg(A)
Res2 = copy.deepcopy(A)
assert np.all((Res1 - Res2) == 0)
assert np.allclose(Res2, [[0, 1], [1, 2]])
assert np.allclose(Res1, [[0, 1], [1, 2]])
def test():
_impl_of_memory_test()
if __name__ == "__main__":
test()
This is a reproducer, that also fails, here the copy operation is omitted, so we get the genuine true output:
import dace
import numpy as np
@dace.program
def program():
# Was originally `program_strides_2()` in `tests/numpy/array_creation_test.py`.
A = dace.ndarray((2, 2), dtype=dace.int32, strides=(1, 2))
for i, j in dace.map[0:2, 0:2]:
A[i, j] = j + i
return A
def _impl_of_memory_test():
sdfg = program.to_sdfg()
csdfg = sdfg.compile()
A1 = csdfg()
assert np.allclose(A1, [[0, 1], [1, 2]])
assert A1.strides == (4, 8)
A2 = csdfg()
assert np.allclose(A2, [[0, 1], [1, 2]])
assert A2.strides == (4, 8)
def test():
_impl_of_memory_test()
if __name__ == "__main__":
test()