@@ -1020,11 +1020,18 @@ def _get_selection(self, indexer, out=None, fields=None):
1020
1020
check_array_shape ('out' , out , out_shape )
1021
1021
1022
1022
# iterate over chunks
1023
- for chunk_coords , chunk_selection , out_selection in indexer :
1023
+ if not hasattr (self .chunk_store , "getitems" ):
1024
+ # sequentially get one key at a time from storage
1025
+ for chunk_coords , chunk_selection , out_selection in indexer :
1024
1026
1025
- # load chunk selection into output array
1026
- self ._chunk_getitem (chunk_coords , chunk_selection , out , out_selection ,
1027
- drop_axes = indexer .drop_axes , fields = fields )
1027
+ # load chunk selection into output array
1028
+ self ._chunk_getitem (chunk_coords , chunk_selection , out , out_selection ,
1029
+ drop_axes = indexer .drop_axes , fields = fields )
1030
+ else :
1031
+ # allow storage to get multiple items at once
1032
+ lchunk_coords , lchunk_selection , lout_selection = zip (* indexer )
1033
+ self ._chunk_getitems (lchunk_coords , lchunk_selection , out , lout_selection ,
1034
+ drop_axes = indexer .drop_axes , fields = fields )
1028
1035
1029
1036
if out .shape :
1030
1037
return out
@@ -1548,6 +1555,52 @@ def _set_selection(self, indexer, value, fields=None):
1548
1555
# put data
1549
1556
self ._chunk_setitem (chunk_coords , chunk_selection , chunk_value , fields = fields )
1550
1557
1558
+ def _process_chunk (self , out , cdata , chunk_selection , drop_axes ,
1559
+ out_is_ndarray , fields , out_selection ):
1560
+ """Take binary data from storage and fill output array"""
1561
+ if (out_is_ndarray and
1562
+ not fields and
1563
+ is_contiguous_selection (out_selection ) and
1564
+ is_total_slice (chunk_selection , self ._chunks ) and
1565
+ not self ._filters and
1566
+ self ._dtype != object ):
1567
+
1568
+ dest = out [out_selection ]
1569
+ write_direct = (
1570
+ dest .flags .writeable and
1571
+ (
1572
+ (self ._order == 'C' and dest .flags .c_contiguous ) or
1573
+ (self ._order == 'F' and dest .flags .f_contiguous )
1574
+ )
1575
+ )
1576
+
1577
+ if write_direct :
1578
+
1579
+ # optimization: we want the whole chunk, and the destination is
1580
+ # contiguous, so we can decompress directly from the chunk
1581
+ # into the destination array
1582
+
1583
+ if self ._compressor :
1584
+ self ._compressor .decode (cdata , dest )
1585
+ else :
1586
+ chunk = ensure_ndarray (cdata ).view (self ._dtype )
1587
+ chunk = chunk .reshape (self ._chunks , order = self ._order )
1588
+ np .copyto (dest , chunk )
1589
+ return
1590
+
1591
+ # decode chunk
1592
+ chunk = self ._decode_chunk (cdata )
1593
+
1594
+ # select data from chunk
1595
+ if fields :
1596
+ chunk = chunk [fields ]
1597
+ tmp = chunk [chunk_selection ]
1598
+ if drop_axes :
1599
+ tmp = np .squeeze (tmp , axis = drop_axes )
1600
+
1601
+ # store selected data in output
1602
+ out [out_selection ] = tmp
1603
+
1551
1604
def _chunk_getitem (self , chunk_coords , chunk_selection , out , out_selection ,
1552
1605
drop_axes = None , fields = None ):
1553
1606
"""Obtain part or whole of a chunk.
@@ -1568,15 +1621,14 @@ def _chunk_getitem(self, chunk_coords, chunk_selection, out, out_selection,
1568
1621
TODO
1569
1622
1570
1623
"""
1571
-
1572
- assert len (chunk_coords ) == len (self ._cdata_shape )
1573
-
1574
1624
out_is_ndarray = True
1575
1625
try :
1576
1626
out = ensure_ndarray (out )
1577
1627
except TypeError :
1578
1628
out_is_ndarray = False
1579
1629
1630
+ assert len (chunk_coords ) == len (self ._cdata_shape )
1631
+
1580
1632
# obtain key for chunk
1581
1633
ckey = self ._chunk_key (chunk_coords )
1582
1634
@@ -1594,48 +1646,36 @@ def _chunk_getitem(self, chunk_coords, chunk_selection, out, out_selection,
1594
1646
out [out_selection ] = fill_value
1595
1647
1596
1648
else :
1649
+ self ._process_chunk (out , cdata , chunk_selection , drop_axes ,
1650
+ out_is_ndarray , fields , out_selection )
1597
1651
1598
- if (out_is_ndarray and
1599
- not fields and
1600
- is_contiguous_selection (out_selection ) and
1601
- is_total_slice (chunk_selection , self ._chunks ) and
1602
- not self ._filters and
1603
- self ._dtype != object ):
1604
-
1605
- dest = out [out_selection ]
1606
- write_direct = (
1607
- dest .flags .writeable and (
1608
- (self ._order == 'C' and dest .flags .c_contiguous ) or
1609
- (self ._order == 'F' and dest .flags .f_contiguous )
1610
- )
1611
- )
1612
-
1613
- if write_direct :
1652
+ def _chunk_getitems (self , lchunk_coords , lchunk_selection , out , lout_selection ,
1653
+ drop_axes = None , fields = None ):
1654
+ """As _chunk_getitem, but for lists of chunks
1614
1655
1615
- # optimization: we want the whole chunk, and the destination is
1616
- # contiguous, so we can decompress directly from the chunk
1617
- # into the destination array
1656
+ This gets called where the storage supports ``getitems``, so that
1657
+ it can decide how to fetch the keys, allowing concurrency.
1658
+ """
1659
+ out_is_ndarray = True
1660
+ try :
1661
+ out = ensure_ndarray (out )
1662
+ except TypeError : # pragma: no cover
1663
+ out_is_ndarray = False
1618
1664
1619
- if self ._compressor :
1620
- self ._compressor .decode (cdata , dest )
1665
+ ckeys = [self ._chunk_key (ch ) for ch in lchunk_coords ]
1666
+ cdatas = self .chunk_store .getitems (ckeys )
1667
+ for ckey , chunk_select , out_select in zip (ckeys , lchunk_selection , lout_selection ):
1668
+ if ckey in cdatas :
1669
+ self ._process_chunk (out , cdatas [ckey ], chunk_select , drop_axes ,
1670
+ out_is_ndarray , fields , out_select )
1671
+ else :
1672
+ # check exception type
1673
+ if self ._fill_value is not None :
1674
+ if fields :
1675
+ fill_value = self ._fill_value [fields ]
1621
1676
else :
1622
- chunk = ensure_ndarray (cdata ).view (self ._dtype )
1623
- chunk = chunk .reshape (self ._chunks , order = self ._order )
1624
- np .copyto (dest , chunk )
1625
- return
1626
-
1627
- # decode chunk
1628
- chunk = self ._decode_chunk (cdata )
1629
-
1630
- # select data from chunk
1631
- if fields :
1632
- chunk = chunk [fields ]
1633
- tmp = chunk [chunk_selection ]
1634
- if drop_axes :
1635
- tmp = np .squeeze (tmp , axis = drop_axes )
1636
-
1637
- # store selected data in output
1638
- out [out_selection ] = tmp
1677
+ fill_value = self ._fill_value
1678
+ out [out_select ] = fill_value
1639
1679
1640
1680
def _chunk_setitem (self , chunk_coords , chunk_selection , value , fields = None ):
1641
1681
"""Replace part or whole of a chunk.
0 commit comments