Skip to content

Commit 41964e6

Browse files
committed
misc: Update memory report format
1 parent f00ec9c commit 41964e6

3 files changed

Lines changed: 51 additions & 37 deletions

File tree

devito/operator/operator.py

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,6 @@ def _prepare_arguments(self, autotune=None, estimate_memory=False, **kwargs):
630630
if p.name in args:
631631
# E.g., SubFunctions
632632
continue
633-
# print(p._arg_values(**kwargs)) # Trigger first-touch
634633
for k, v in p._arg_values(estimate_memory=estimate_memory, **kwargs).items():
635634
if k not in args:
636635
args[k] = v
@@ -659,7 +658,6 @@ def _prepare_arguments(self, autotune=None, estimate_memory=False, **kwargs):
659658
# the subsequent phases of the arguments processing
660659
args = kwargs['args'] = ArgumentsMap(args, grid, self)
661660

662-
# FIXME: Will want to remove this if not using prepare_args to estimate memory
663661
if estimate_memory:
664662
# No need to do anything more if only checking the memory
665663
return args
@@ -888,30 +886,38 @@ def estimate_memory(self, human_readable=True, **kwargs):
888886
args = self._prepare_arguments(estimate_memory=True, **kwargs)
889887
mem = args.nbytes_consumed
890888

889+
# Extra information for enhanced operators
890+
extras = self._enrich_memreport(args, human_readable=human_readable)
891+
891892
if human_readable:
892893
headline = f"Memory consumption for operator `{self.name}`:"
893894
w = len(headline)
894895
# Columns are width 10
895-
fdisk = str(humanbytes(mem[disk_layer])).center(10)
896896
fhost = str(humanbytes(mem[host_layer])).center(10)
897897
fdevice = str(humanbytes(mem[device_layer])).center(10)
898898

899-
# TODO: There is nominally a table generator in pytools which is a dependency
900-
# of a dependency and thus in the env anyway
901-
info(
899+
memreport = (
902900
"\n"
903901
f"{headline}\n"
904-
f"{'┌──────────┬──────────┬──────────┐'.center(w)}\n"
905-
f"{'│ Disk │ Host │ Device │'.center(w)}\n"
906-
f"{'├──────────┼──────────┼──────────┤'.center(w)}\n"
907-
f"{f'│{fdisk}{fhost}{fdevice}│'.center(w)}\n"
908-
f"{'└──────────┴──────────┴──────────┘'.center(w)}\n"
902+
f"{'┌──────────┬──────────┐'.center(w)}\n"
903+
f"{'│ Host │ Device │'.center(w)}\n"
904+
f"{'├──────────┼──────────┤'.center(w)}\n"
905+
f"{f'│{fhost}{fdevice}│'.center(w)}\n"
906+
f"{'└──────────┴──────────┘'.center(w)}\n"
909907
)
910908

911909
# TODO: add hinting if the specified operator won't fit
912-
913910
else:
914-
info(f"{self.name} {mem[disk_layer]} {mem[host_layer]} {mem[device_layer]}")
911+
memreport = f"{self.name} {mem[host_layer]} {mem[device_layer]}"
912+
913+
if extras is not None:
914+
memreport += extras
915+
916+
info(memreport)
917+
918+
def _enrich_memreport(self, args, human_readable=True):
919+
# Hook for enriching memory report
920+
pass
915921

916922
def apply(self, **kwargs):
917923
"""
@@ -1338,7 +1344,6 @@ def nbytes_avail_mapper(self):
13381344
"""
13391345
mapper = {}
13401346

1341-
# TODO: This doesn't account for the size of the snapshots?
13421347
# The amount of space available on the disk
13431348
usage = shutil.disk_usage(gettempdir())
13441349
mapper[disk_layer] = usage.free
@@ -1356,17 +1361,12 @@ def nbytes_avail_mapper(self):
13561361
mapper[host_layer] = int(ANYCPU.memavail() / nproc)
13571362

13581363
for layer in (host_layer, device_layer):
1359-
try:
1360-
mapper[layer] -= self.nbytes_consumed_operator[layer]
1361-
except KeyError:
1362-
continue
1364+
mapper[layer] -= self.nbytes_consumed_operator.get(layer, 0)
13631365

13641366
mapper = {k: int(v) for k, v in mapper.items()}
13651367

13661368
return mapper
13671369

1368-
# TODO: This will want some suitable tests in due course
1369-
# TODO: Might want to also check the spillover onto disk
13701370
@cached_property
13711371
def nbytes_consumed(self):
13721372
"""Memory consumed by all objects in the operator"""
@@ -1415,17 +1415,13 @@ def get_nbytes(obj):
14151415
and not i.is_ArrayBasic and not i.alias]
14161416

14171417
for i in op_symbols:
1418-
# FIXME: Probably wrong for streamed functions
1419-
# TODO: Need a hook for PRO here
14201418
# Will overreport memory usage currently
14211419
try:
14221420
# TODO: is _obj even needed?
14231421
v = get_nbytes(self[i.name]._obj)
14241422
except AttributeError:
14251423
v = get_nbytes(self.get(i.name, i))
14261424

1427-
print(i, humanbytes(v))
1428-
14291425
if i._mem_host or i._mem_mapped:
14301426
# No need to add to device , as it will be counted
14311427
# by nbytes_consumed_memmapped
@@ -1462,8 +1458,6 @@ def nbytes_consumed_array(self):
14621458
# E.g. the Arrays used to store the MPI halo exchanges
14631459
continue
14641460

1465-
print(i, humanbytes(v))
1466-
14671461
if i._mem_host:
14681462
host += v
14691463
elif i._mem_local:
@@ -1504,6 +1498,25 @@ def nbytes_consumed_memmapped(self):
15041498

15051499
return {disk_layer: 0, host_layer: 0, device_layer: device}
15061500

1501+
@cached_property
1502+
def nbytes_snapshots(self):
1503+
1504+
# Symbols in the operator which may or may not carry data
1505+
op_symbols = FindSymbols().visit(self.op)
1506+
1507+
# Filter to streamed functions
1508+
op_symbols = [i for i in op_symbols if i.is_AbstractFunction
1509+
and not i.is_ArrayBasic and not i.alias]
1510+
1511+
disk = 0
1512+
for i in op_symbols:
1513+
try:
1514+
disk += i.size_snapshot*i._time_size_ideal*np.dtype(i.dtype).itemsize
1515+
except AttributeError:
1516+
pass
1517+
1518+
return {disk_layer: disk, host_layer: 0, device_layer: 0}
1519+
15071520

15081521
def parse_kwargs(**kwargs):
15091522
"""

devito/types/sparse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ def _arg_defaults(self, alias=None, estimate_memory=False):
652652
if f is not None:
653653
mapper[getattr(self, i)] = f
654654

655-
if estimate_memory: # kwargs.get("estimate_memory", False):
655+
if estimate_memory:
656656
# Avoid touching the data in any capacity, and simply return
657657
# the symbolic objects if merely estimating memory consumption.
658658
return ReducerMap({v.name: k for k, v in mapper.items()})

tests/test_operator.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,8 +2073,9 @@ def parse_output(self, output, expected):
20732073
# Check that no allocation occurs as estimate_memory should avoid data touch
20742074
assert "Allocating" not in output.text
20752075

2076-
name, disk, host, device = output.records[-1].message.split()
2077-
extracted = (name, int(disk), int(host), int(device))
2076+
parsed = output.records[-1].message.split()
2077+
name, host, device = parsed[:3]
2078+
extracted = (name, int(host), int(device))
20782079

20792080
assert extracted == expected
20802081

@@ -2093,7 +2094,7 @@ def test_basic_usage(self, caplog, shape, dtype, so):
20932094

20942095
# Check output of estimate_memory
20952096
host = reduce(mul, f.shape_allocated)*np.dtype(f.dtype).itemsize
2096-
expected = ("Kernel", 0, host, 0)
2097+
expected = ("Kernel", host, 0)
20972098
self.parse_output(caplog, expected)
20982099

20992100
def test_multiple_objects(self, caplog):
@@ -2107,7 +2108,7 @@ def test_multiple_objects(self, caplog):
21072108

21082109
check = sum(reduce(mul, func.shape_allocated)*np.dtype(func.dtype).itemsize
21092110
for func in (f, g))
2110-
expected = ("Kernel", 0, check, 0)
2111+
expected = ("Kernel", check, 0)
21112112
self.parse_output(caplog, expected)
21122113

21132114
@pytest.mark.parametrize('time', [True, False])
@@ -2126,7 +2127,7 @@ def test_sparse(self, caplog, time):
21262127

21272128
check = sum(reduce(mul, func.shape_allocated)*np.dtype(func.dtype).itemsize
21282129
for func in (f, src, src.coordinates))
2129-
expected = ("Kernel", 0, check, 0)
2130+
expected = ("Kernel", check, 0)
21302131
self.parse_output(caplog, expected)
21312132

21322133
@pytest.mark.parametrize('save', [None, Buffer(3), 10])
@@ -2138,7 +2139,7 @@ def test_timefunction(self, caplog, save):
21382139
op = Operator(Eq(f, 1))
21392140
op.estimate_memory(human_readable=False)
21402141
check = reduce(mul, f.shape_allocated)*np.dtype(f.dtype).itemsize
2141-
expected = ("Kernel", 0, check, 0)
2142+
expected = ("Kernel", check, 0)
21422143
self.parse_output(caplog, expected)
21432144

21442145
def test_mashup(self, caplog):
@@ -2162,7 +2163,7 @@ def test_mashup(self, caplog):
21622163
check = sum(reduce(mul, func.shape_allocated)*np.dtype(func.dtype).itemsize
21632164
for func in (f, g, src0, src0.coordinates,
21642165
src1, src1.coordinates))
2165-
expected = ("Kernel", 0, check, 0)
2166+
expected = ("Kernel", check, 0)
21662167
self.parse_output(caplog, expected)
21672168

21682169
def test_temp_array(self, caplog):
@@ -2194,7 +2195,7 @@ def test_temp_array(self, caplog):
21942195
# Factor in the temp array
21952196
check += reduce(mul, b.shape_allocated)*np.dtype(a.dtype).itemsize
21962197

2197-
expected = ("Kernel", 0, check, 0)
2198+
expected = ("Kernel", check, 0)
21982199
self.parse_output(caplog, expected)
21992200

22002201
def test_overrides(self, caplog):
@@ -2226,7 +2227,7 @@ def test_overrides(self, caplog):
22262227
check = sum(reduce(mul, func.shape_allocated)*np.dtype(func.dtype).itemsize
22272228
for func in (f1, tf1, s1, s1.coordinates, st1, st1.coordinates))
22282229

2229-
expected = ("Kernel", 0, check, 0)
2230+
expected = ("Kernel", check, 0)
22302231
self.parse_output(caplog, expected)
22312232

22322233
def test_device(self, caplog):
@@ -2249,5 +2250,5 @@ def test_device(self, caplog):
22492250
check = reduce(mul, f.shape_allocated)*np.dtype(f.dtype).itemsize
22502251

22512252
# Matching memory allocated both on host and device for memmap
2252-
expected = ("Kernel", 0, check, check)
2253+
expected = ("Kernel", check, check)
22532254
self.parse_output(caplog, expected)

0 commit comments

Comments
 (0)