Skip to content

Commit 50249cd

Browse files
authored
Merge pull request #445 from LSSTDESC/u/jchiang/checkpoint_fixes
Checkpoint tweaks
2 parents 87dd06c + eb0f63b commit 50249cd

3 files changed

Lines changed: 75 additions & 9 deletions

File tree

config/imsim-config.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,15 @@ image:
126126
# a small numer of photons.
127127

128128
# The objects are processed in batches.
129-
# If checkpointing is turned on, then the checkpoint file will be updated after each batch.
129+
# If checkpointing is turned on, then the checkpoint file will be updated after every
130+
# group of nbatch_per_checkpoint batches. This factor helps lower the overall checkpoint
131+
# rate when running thousands of processes concurrently; otherwise, the contention
132+
# from lots of concurrent checkpoint writes can overwhelm the file system.
130133
# Even if not checkpointing, using batches helps keep the memory down, since a bunch of
131134
# temporary data can be purged after each batch.
132135
# The default number of batches is 100, but you can change it if desired.
133136
nbatch: 100
137+
nbatch_per_checkpoint: 1
134138

135139
det_name: "$det_name"
136140

imsim/lsst_image.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def setup(self, config, base, image_num, obj_num, ignore, logger):
4545
req = { 'det_name': str }
4646
opt = { 'size': int , 'xsize': int , 'ysize': int, 'dtype': None,
4747
'apply_sky_gradient': bool, 'apply_fringing': bool,
48-
'boresight': galsim.CelestialCoord, 'camera': str, 'nbatch': int}
48+
'boresight': galsim.CelestialCoord, 'camera': str, 'nbatch': int,
49+
'nbatch_per_checkpoint': int}
4950
params = GetAllParams(config, base, req=req, opt=opt, ignore=ignore+extra_ignore)[0]
5051

5152
# Let the user override the image size
@@ -86,6 +87,7 @@ def setup(self, config, base, image_num, obj_num, ignore, logger):
8687
try:
8788
self.checkpoint = galsim.config.GetInputObj('checkpoint', config, base, 'LSST_Image')
8889
self.nbatch = params.get('nbatch', 100)
90+
self.nbatch_per_checkpoint = params.get('nbatch_per_checkpoint', 1)
8991
except galsim.config.GalSimConfigError:
9092
self.checkpoint = None
9193
# Batching is also useful for memory reasons, to limit the number of stamps held
@@ -149,7 +151,8 @@ def buildImage(self, config, base, image_num, obj_num, logger):
149151
chk_name = 'buildImage_%s'%(self.det_name)
150152
saved = self.checkpoint.load(chk_name)
151153
if saved is not None:
152-
full_image, all_bounds, all_vars, start_num, extra_builder = saved
154+
full_image, all_bounds, all_vars, delta_start_num, extra_builder = saved
155+
start_num = delta_start_num + base.get('start_obj_num', 0)
153156
if extra_builder is not None:
154157
base['extra_builder'] = extra_builder
155158
all_stamps = [galsim._Image(np.array([]), b, full_image.wcs) for b in all_bounds]
@@ -223,13 +226,15 @@ def buildImage(self, config, base, image_num, obj_num, logger):
223226
# Don't save the full stamps. All we need for FlattenNoiseVariance is the bounds.
224227
# Everything else about the stamps has already been handled above.
225228
all_bounds = [stamp.bounds for stamp in all_stamps]
226-
data = (full_image, all_bounds, all_vars, end_obj_num,
229+
delta_end_obj_num = end_obj_num - base.get('start_obj_num', 0)
230+
data = (full_image, all_bounds, all_vars, delta_end_obj_num,
227231
base.get('extra_builder',None))
228-
self.checkpoint.save(chk_name, data)
229-
logger.warning('File %d: Completed batch %d with objects [%d, %d), and wrote '
230-
'checkpoint data to %s',
231-
base.get('file_num', 0), batch+1, start_obj_num, end_obj_num,
232-
self.checkpoint.file_name)
232+
logger.warning('File %d: Completed batch %d with objects [%d, %d)',
233+
base.get('file_num', 0), batch+1, start_obj_num, end_obj_num)
234+
if (batch % self.nbatch_per_checkpoint == 0
235+
or batch + 1 == nbatch):
236+
self.checkpoint.save(chk_name, data)
237+
logger.warning('Wrote checkpoint data to %s', self.checkpoint.file_name)
233238

234239
# Bring the image so far up to a flat noise variance
235240
current_var = galsim.config.FlattenNoiseVariance(

tests/test_checkpoint.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
from pathlib import Path
33
import numpy as np
4+
import hashlib
45
import logging
56
import galsim
67
import time
@@ -213,6 +214,62 @@ def test_checkpoint_image():
213214
np.testing.assert_array_equal(centroid5, centroid2)
214215

215216

217+
def test_nbatch_per_checkpoint():
218+
"""Test that the final checkpoint files written with two different values
219+
of nbatch_per_checkpoint, both not factors of nbatch, produce the same
220+
checkpoint output."""
221+
wcs = galsim.PixelScale(0.2)
222+
config = {
223+
'input': {
224+
'checkpoint': {
225+
'dir': 'output',
226+
'file_name': '$"checkpoint_%d.hdf"%image_num',
227+
},
228+
},
229+
'gal': {
230+
'type': 'Exponential',
231+
'half_light_radius': {'type': 'Random', 'min': 0.3, 'max': 1.2},
232+
'ellip': {
233+
'type': 'EBeta',
234+
'e': {'type': 'Random', 'min': 0.0, 'max': 0.3},
235+
'beta': {'type': 'Random'},
236+
}
237+
},
238+
'image': {
239+
'type': 'LSST_Image',
240+
'det_name': 'R22_S11',
241+
'xsize': 2048,
242+
'ysize': 2048,
243+
'wcs': wcs,
244+
'random_seed': 12345,
245+
'nobjects': 500,
246+
'nbatch': 100,
247+
},
248+
}
249+
250+
checkpoint_0 = 'output/checkpoint_0.hdf'
251+
if os.path.exists(checkpoint_0):
252+
os.remove(checkpoint_0)
253+
config['image_num'] = 0
254+
config['image']['nbatch_per_checkpoint'] = 11
255+
galsim.config.ProcessInput(config)
256+
image0 = galsim.config.BuildImage(config)
257+
with open(checkpoint_0, 'rb') as fobj:
258+
md5_0 = hashlib.md5(fobj.read()).hexdigest()
259+
260+
checkpoint_1 = 'output/checkpoint_1.hdf'
261+
if os.path.exists(checkpoint_1):
262+
os.remove(checkpoint_1)
263+
config['image_num'] = 1
264+
config['image']['nbatch_per_checkpoint'] = 13
265+
galsim.config.ProcessInput(config)
266+
image1 = galsim.config.BuildImage(config)
267+
with open(checkpoint_1, 'rb') as fobj:
268+
md5_1 = hashlib.md5(fobj.read()).hexdigest()
269+
270+
assert md5_0 == md5_1
271+
272+
216273
def test_checkpoint_flatten():
217274
"""Test the flatten() step of buildImage when using checkpointing
218275
"""

0 commit comments

Comments
 (0)