Skip to content

Commit

Permalink
Simply style locking.
Browse files Browse the repository at this point in the history
Just reference the unstyled instance directly.  This improves speed by
reducing locking.
  • Loading branch information
manthey committed Jun 27, 2023
1 parent 25625ad commit b157930
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 64 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
### Improvements
- Harden reading some nc files ([#1218](../../pull/1218))
- Increase the cache used for opening directories in the pylibtiff source ([#1221](../../pull/1221))
- Refactor style locking to increase parallelism ([#1224](../../pull/1224))

### Changes
- Clean up some old slideatlas url references ([#1223](../../pull/1223))

### Bug Fixes
- Pass style to histogram endpoint as json ([#1220](../../pull/1220))
Expand Down
5 changes: 3 additions & 2 deletions large_image/cache_util/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,12 @@ def __call__(cls, *args, **kwargs): # noqa - N805
subkwargs.pop('style')
subresult = cls(*args, **subkwargs)
result = subresult.__class__.__new__(subresult.__class__)
with subresult._styleLock:
with subresult._sourceLock:
result.__dict__ = subresult.__dict__.copy()
result._styleLock = threading.RLock()
result._sourceLock = threading.RLock()
result._setStyle(kwargs['style'])
result._classkey = key
result._unstyledInstance = subresult
# for pickling
result._initValues = (args, kwargs.copy())
with cacheLock:
Expand Down
93 changes: 32 additions & 61 deletions large_image/tilesource/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def __init__(self, encoding='JPEG', jpegQuality=95, jpegSubsampling=0,
self.levels = None
self.sizeX = None
self.sizeY = None
self._styleLock = threading.RLock()
self._sourceLock = threading.RLock()
self._dtype = None
self._bandCount = None

Expand Down Expand Up @@ -232,27 +232,23 @@ def style(self):

@property
def dtype(self):
if not self._dtype:
with self._styleLock:
if not hasattr(self, '_skipStyle'):
self._setSkipStyle(True)
try:
sample, format = self.getRegion(
region=dict(left=0, top=0, width=1, height=1),
format=TILE_FORMAT_NUMPY)
self._dtype = sample.dtype
self._bandCount = sample.shape[-1] if len(sample.shape) == 3 else 1
finally:
self._setSkipStyle(False)
else:
return None

with self._sourceLock:
if not self._dtype:
self._dtype = 'check'
sample, _ = getattr(self, '_unstyledInstance', self).getRegion(
region=dict(left=0, top=0, width=1, height=1),
format=TILE_FORMAT_NUMPY)
self._dtype = sample.dtype
self._bandCount = len(
getattr(getattr(self, '_unstyledInstance', self), '_bandInfo', []))
if not self._bandCount:
self._bandCount = sample.shape[-1] if len(sample.shape) == 3 else 1
return self._dtype

@property
def bandCount(self):
if not self._bandCount:
if not self.dtype:
if not self._dtype or str(self._dtype) == 'check':
return None
return self._bandCount

Expand Down Expand Up @@ -1123,21 +1119,17 @@ def _scanForMinMax(self, dtype, frame=None, analysisSize=1024, onlyMinMax=True,
:param onlyMinMax: if True, only find the min and max. If False, get
the entire histogram.
"""
self._setSkipStyle(True)
try:
self._bandRanges[frame] = self.histogram(
dtype=dtype,
onlyMinMax=onlyMinMax,
output={'maxWidth': min(self.sizeX, analysisSize),
'maxHeight': min(self.sizeY, analysisSize)},
resample=False,
frame=frame, **kwargs)
if self._bandRanges[frame]:
self.logger.info('Style range is %r' % {
k: v for k, v in self._bandRanges[frame].items() if k in {
'min', 'max', 'mean', 'stdev'}})
finally:
self._setSkipStyle(False)
self._bandRanges[frame] = getattr(self, '_unstyledInstance', self).histogram(
dtype=dtype,
onlyMinMax=onlyMinMax,
output={'maxWidth': min(self.sizeX, analysisSize),
'maxHeight': min(self.sizeY, analysisSize)},
resample=False,
frame=frame, **kwargs)
if self._bandRanges[frame]:
self.logger.info('Style range is %r' % {
k: v for k, v in self._bandRanges[frame].items() if k in {
'min', 'max', 'mean', 'stdev'}})

def _validateMinMaxValue(self, value, frame, dtype):
"""
Expand Down Expand Up @@ -1392,21 +1384,6 @@ def _applyICCProfile(self, sc, frame):
self.logger.exception('Failed to apply ICC profile')
return sc.iccimage

def _setSkipStyle(self, setSkip=False):
if not hasattr(self, '_classkey'):
self._classkey = self.getState()
if setSkip:
self._unlocked_classkey = self._classkey
if hasattr(self, 'cache_lock'):
with self.cache_lock:
self._classkeyLock = self._styleLock
self._skipStyle = True
# Divert the tile cache while querying unstyled tiles
self._classkey = self._unstyledClassKey()
else:
del self._skipStyle
self._classkey = self._unlocked_classkey

def _applyStyle(self, image, style, x, y, z, frame=None): # noqa
"""
Apply a style to a numpy image.
Expand Down Expand Up @@ -1450,14 +1427,11 @@ def _applyStyle(self, image, style, x, y, z, frame=None): # noqa
else:
frame = entry['frame'] if entry.get('frame') is not None else (
sc.mainFrame + entry['framedelta'])
self._setSkipStyle(True)
try:
image = self.getTile(x, y, z, frame=frame, numpyAllowed=True)
image = image[:sc.mainImage.shape[0],
:sc.mainImage.shape[1],
:sc.mainImage.shape[2]]
finally:
self._setSkipStyle(False)
image = getattr(self, '_unstyledInstance', self).getTile(
x, y, z, frame=frame, numpyAllowed=True)
image = image[:sc.mainImage.shape[0],
:sc.mainImage.shape[1],
:sc.mainImage.shape[2]]
if (isinstance(entry.get('band'), int) and
entry['band'] >= 1 and entry['band'] <= image.shape[2]):
sc.bandidx = entry['band'] - 1
Expand Down Expand Up @@ -1566,9 +1540,7 @@ def _outputTileNumpyStyle(self, tile, applyStyle, x, y, z, frame=None):
"""
tile, mode = _imageToNumpy(tile)
if applyStyle and (getattr(self, 'style', None) or hasattr(self, '_iccprofiles')):
with self._styleLock:
if not getattr(self, '_skipStyle', False):
tile = self._applyStyle(tile, getattr(self, 'style', None), x, y, z, frame)
tile = self._applyStyle(tile, getattr(self, 'style', None), x, y, z, frame)
if tile.shape[0] != self.tileHeight or tile.shape[1] != self.tileWidth:
extend = numpy.zeros(
(self.tileHeight, self.tileWidth, tile.shape[2]),
Expand Down Expand Up @@ -1611,7 +1583,7 @@ def _outputTile(self, tile, tileEncoding, x, y, z, pilImageAllowed=False,
not isEdge and (not applyStyle or not hasStyle)):
return tile

if self._dtype is None:
if self._dtype is None or str(self._dtype) == 'check':
if tileEncoding == TILE_FORMAT_NUMPY:
self._dtype = tile.dtype
self._bandCount = tile.shape[-1] if len(tile.shape) == 3 else 1
Expand Down Expand Up @@ -1854,8 +1826,7 @@ def _getFrame(self, frame=None, **kwargs):
:returns: an integer frame number.
"""
frame = int(frame or 0)
if (not getattr(self, '_skipStyle', None) and
hasattr(self, '_style') and 'bands' in self.style and
if (hasattr(self, '_style') and 'bands' in self.style and
len(self.style['bands']) and
all(entry.get('frame') is not None for entry in self.style['bands'])):
frame = int(self.style['bands'][0]['frame'])
Expand Down
3 changes: 2 additions & 1 deletion sources/rasterio/large_image_source_rasterio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,8 @@ def getBandInformation(self, statistics=True, dataset=None, **kwargs):
infoSet[i] = {k: v for k, v in info.items() if v not in (None, '')}

# set the value to cache if needed
cache is False or getattr(self, '_bandInfo', infoSet)
if cache:
self._bandInfo = infoSet

return infoSet

Expand Down

0 comments on commit b157930

Please sign in to comment.