2020 NODE , CELL , dimensions , configuration , TensorFunction ,
2121 TensorTimeFunction , VectorFunction , VectorTimeFunction ,
2222 div , grad , switchconfig , exp , Buffer )
23- from devito import Inc , Le , Lt , Ge , Gt # noqa
23+ from devito import Inc , Le , Lt , Ge , Gt , sin # noqa
24+ from devito .arch .archinfo import Device
2425from devito .exceptions import InvalidOperator
2526from devito .finite_differences .differentiable import diff2sympy
2627from devito .ir .equations import ClusterizedEq
@@ -2066,12 +2067,17 @@ def test_indirection(self):
20662067class TestEstimateMemory :
20672068 """Tests for the Operator.estimate_memory() utility"""
20682069
2069- _array_temp = "r0L0(x, y) " if "CXX" in configuration ['language' ] else "r0[x][y] "
2070+ _array_temp = "r0L0(" if "CXX" in configuration ['language' ] else "r0["
20702071
2071- def parse_output (self , summary , expected ):
2072- """Parse estimate_memory machine-readable output"""
2072+ def parse_output (self , summary , check , arrays = 0 ):
2073+ device = isinstance (configuration ['platform' ], Device )
2074+ expected = ((check , check + arrays ) if device else (check + arrays , 0 ))
20732075 assert (summary ['host' ], summary ['device' ]) == expected
20742076
2077+ def sum_sizes (self , funcs ):
2078+ return sum (reduce (mul , func .shape_allocated )* np .dtype (func .dtype ).itemsize
2079+ for func in funcs )
2080+
20752081 @pytest .mark .parametrize ('shape' , [(11 ,), (101 , 101 ), (101 , 101 , 101 )])
20762082 @pytest .mark .parametrize ('dtype' , [np .int8 , np .int16 , np .float32 ,
20772083 np .float32 , np .complex64 ])
@@ -2088,8 +2094,7 @@ def test_basic_usage(self, caplog, shape, dtype, so):
20882094
20892095 # Check output of estimate_memory
20902096 host = reduce (mul , f .shape_allocated )* np .dtype (f .dtype ).itemsize
2091- expected = (host , 0 )
2092- self .parse_output (summary , expected )
2097+ self .parse_output (summary , host )
20932098
20942099 def test_multiple_objects (self , caplog ):
20952100 grid = Grid (shape = (101 , 101 ))
@@ -2101,10 +2106,8 @@ def test_multiple_objects(self, caplog):
21012106 summary = op .estimate_memory ()
21022107 assert "Allocating" not in caplog .text
21032108
2104- check = sum (reduce (mul , func .shape_allocated )* np .dtype (func .dtype ).itemsize
2105- for func in (f , g ))
2106- expected = (check , 0 )
2107- self .parse_output (summary , expected )
2109+ check = self .sum_sizes ((f , g ))
2110+ self .parse_output (summary , check )
21082111
21092112 @pytest .mark .parametrize ('time' , [True , False ])
21102113 def test_sparse (self , caplog , time ):
@@ -2121,10 +2124,8 @@ def test_sparse(self, caplog, time):
21212124 summary = op .estimate_memory ()
21222125 assert "Allocating" not in caplog .text
21232126
2124- check = sum (reduce (mul , func .shape_allocated )* np .dtype (func .dtype ).itemsize
2125- for func in (f , src , src .coordinates ))
2126- expected = (check , 0 )
2127- self .parse_output (summary , expected )
2127+ check = self .sum_sizes ((f , src , src .coordinates ))
2128+ self .parse_output (summary , check )
21282129
21292130 @pytest .mark .parametrize ('save' , [None , Buffer (3 ), 10 ])
21302131 def test_timefunction (self , caplog , save ):
@@ -2136,8 +2137,7 @@ def test_timefunction(self, caplog, save):
21362137 summary = op .estimate_memory ()
21372138 assert "Allocating" not in caplog .text
21382139 check = reduce (mul , f .shape_allocated )* np .dtype (f .dtype ).itemsize
2139- expected = (check , 0 )
2140- self .parse_output (summary , expected )
2140+ self .parse_output (summary , check )
21412141
21422142 def test_mashup (self , caplog ):
21432143 grid = Grid (shape = (101 , 101 ))
@@ -2158,11 +2158,8 @@ def test_mashup(self, caplog):
21582158 summary = op .estimate_memory ()
21592159 assert "Allocating" not in caplog .text
21602160
2161- check = sum (reduce (mul , func .shape_allocated )* np .dtype (func .dtype ).itemsize
2162- for func in (f , g , src0 , src0 .coordinates ,
2163- src1 , src1 .coordinates ))
2164- expected = (check , 0 )
2165- self .parse_output (summary , expected )
2161+ check = self .sum_sizes ((f , g , src0 , src0 .coordinates , src1 , src1 .coordinates ))
2162+ self .parse_output (summary , check )
21662163
21672164 @pytest .mark .parametrize ('override' , [True , False ])
21682165 def test_temp_array (self , caplog , override ):
@@ -2188,8 +2185,8 @@ def test_temp_array(self, caplog, override):
21882185 b = Function (name = 'b' , grid = grid , space_order = 0 )
21892186
21902187 # Reuse an expensive function to encourage generation of an array temp
2191- eq0 = Eq (f .forward , g + sympy . sin (a ))
2192- eq1 = Eq (g .forward , f + sympy . sin (a ))
2188+ eq0 = Eq (f .forward , g + sin (a ). dx )
2189+ eq1 = Eq (g .forward , f + sin (a ). dx )
21932190
21942191 with switchconfig (log_level = 'DEBUG' ), caplog .at_level (logging .DEBUG ):
21952192 op = Operator ([eq0 , eq1 ])
@@ -2201,37 +2198,32 @@ def test_temp_array(self, caplog, override):
22012198 summary = op .estimate_memory (** kwargs )
22022199 assert "Allocating" not in caplog .text
22032200
2204- check = sum (reduce (mul , func .shape_allocated )* np .dtype (func .dtype ).itemsize
2205- for func in funcs )
2201+ check = self .sum_sizes (funcs )
22062202
22072203 # Factor in the temp array
2208- check += reduce (mul , b .shape_allocated )* np .dtype (b .dtype ).itemsize
2209-
2210- expected = (check , 0 )
2211- self .parse_output (summary , expected )
2204+ # Note: temp array size is incremented by one in the x dimension
2205+ # due to derivative.
2206+ array_check = (b .shape_allocated [0 ]+ 1 )* b .shape_allocated [1 ]
2207+ array_check *= np .dtype (b .dtype ).itemsize
2208+ self .parse_output (summary , check , arrays = array_check )
22122209
22132210 def test_overrides (self , caplog ):
2214- # TODO: Consolidate this boilerplate
2215- grid0 = Grid (shape = (101 , 101 ))
2211+ def setup (size , npoint , nt , counter ):
2212+ grid = Grid (shape = (size , size ))
2213+ # Original fields
2214+ f = Function (name = f'f{ counter } ' , grid = grid , space_order = 4 )
2215+ tf = TimeFunction (name = f'tf{ counter } ' , grid = grid , space_order = 4 )
2216+ s = SparseFunction (name = f's{ counter } ' , grid = grid , npoint = npoint )
2217+ st = SparseTimeFunction (name = f'st{ counter } ' , grid = grid , npoint = npoint , nt = nt )
2218+
2219+ return f , tf , s , st
2220+
22162221 # Original fields
2217- f0 = Function (name = 'f0' , grid = grid0 , space_order = 4 )
2218- tf0 = TimeFunction (name = 'tf0' , grid = grid0 , space_order = 4 )
2219- s0 = SparseFunction (name = 's0' , grid = grid0 , npoint = 100 )
2220- st0 = SparseTimeFunction (name = 'st0' , grid = grid0 , npoint = 100 , nt = 10 )
2221-
2222- grid1 = Grid (shape = (201 , 201 )) # Bigger grid so overrides are distinct
2223- # Replacement fields
2224- f1 = Function (name = 'f1' , grid = grid1 , space_order = 4 )
2225- tf1 = TimeFunction (name = 'tf1' , grid = grid1 , space_order = 4 )
2226- s1 = SparseFunction (name = 's1' , grid = grid1 , npoint = 200 )
2227- st1 = SparseTimeFunction (name = 'st1' , grid = grid1 , npoint = 200 , nt = 20 )
2228-
2229- grid2 = Grid (shape = (51 , 51 )) # Smaller grid so overrides are distinct
2230- # Alternative replacement fields
2231- f2 = Function (name = 'f2' , grid = grid2 , space_order = 4 )
2232- tf2 = TimeFunction (name = 'tf2' , grid = grid2 , space_order = 4 )
2233- s2 = SparseFunction (name = 's2' , grid = grid2 , npoint = 50 )
2234- st2 = SparseTimeFunction (name = 'st2' , grid = grid2 , npoint = 50 , nt = 5 )
2222+ f0 , tf0 , s0 , st0 = setup (101 , 100 , 10 , 0 )
2223+ # Replacement fields with bigger grid, etc
2224+ f1 , tf1 , s1 , st1 = setup (201 , 200 , 20 , 1 )
2225+ # Replacement fields with smaller grid, etc
2226+ f2 , tf2 , s2 , st2 = setup (51 , 50 , 5 , 2 )
22352227
22362228 eq0 = Eq (f0 , 1 )
22372229 eq1 = Eq (tf0 , 1 )
@@ -2244,21 +2236,15 @@ def test_overrides(self, caplog):
22442236 # Apply overrides for the check
22452237 summary0 = op .estimate_memory (f0 = f1 , tf0 = tf1 , s0 = s1 , st0 = st1 )
22462238
2247- check0 = sum (reduce (mul , func .shape_allocated )* np .dtype (func .dtype ).itemsize
2248- for func in (f1 , tf1 , s1 , s1 .coordinates , st1 , st1 .coordinates ))
2249-
2250- expected0 = (check0 , 0 )
2251- self .parse_output (summary0 , expected0 )
2239+ check0 = self .sum_sizes ((f1 , tf1 , s1 , s1 .coordinates , st1 , st1 .coordinates ))
2240+ self .parse_output (summary0 , check0 )
22522241
22532242 # Check with a second set of overrides
22542243 summary1 = op .estimate_memory (f0 = f2 , tf0 = tf2 , s0 = s2 , st0 = st2 )
22552244 assert "Allocating" not in caplog .text
22562245
2257- check1 = sum (reduce (mul , func .shape_allocated )* np .dtype (func .dtype ).itemsize
2258- for func in (f2 , tf2 , s2 , s2 .coordinates , st2 , st2 .coordinates ))
2259-
2260- expected1 = (check1 , 0 )
2261- self .parse_output (summary1 , expected1 )
2246+ check1 = self .sum_sizes ((f2 , tf2 , s2 , s2 .coordinates , st2 , st2 .coordinates ))
2247+ self .parse_output (summary1 , check1 )
22622248
22632249 def test_device (self , caplog ):
22642250 # Note: this uses switchconfig and runs on all backends to reflect expected
@@ -2269,7 +2255,7 @@ def test_device(self, caplog):
22692255
22702256 f = Function (name = 'f' , grid = grid , space_order = 2 )
22712257
2272- # Compiler is never invoked, so this should be fine
2258+ # Compiler is never invoked, so this is fine
22732259 config = {'log_level' : 'DEBUG' , 'language' : 'openacc' ,
22742260 'platform' : 'nvidiaX' }
22752261 with switchconfig (** config ), caplog .at_level (logging .DEBUG ):
@@ -2281,5 +2267,4 @@ def test_device(self, caplog):
22812267 check = reduce (mul , f .shape_allocated )* np .dtype (f .dtype ).itemsize
22822268
22832269 # Matching memory allocated both on host and device for memmap
2284- expected = (check , check )
2285- self .parse_output (summary , expected )
2270+ self .parse_output (summary , check )
0 commit comments