Skip to content

Commit 0c1e2a1

Browse files
authored
Firefly-1926: Merge PR #1900 from FIREFLY-1926-concurrent-grid
Firefly-1926: optimizing tile generation, transfer, and memory usage
2 parents 2442d49 + 1ef378f commit 0c1e2a1

20 files changed

+762
-334
lines changed

src/firefly/java/edu/caltech/ipac/firefly/data/ServerParams.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ public class ServerParams {
198198
public static final String BACK_TO_URL= "backToUrl";
199199
public static final String MASK_DATA= "maskData";
200200
public static final String MASK_BITS= "maskBits";
201+
public static final String TILE_ACTION= "tileAction";
202+
public static final String TILE_NUMBER= "tileNumber";
201203

202204
//Workspaces
203205
public static final String WS_LIST = "wsList"; // Gets the list of content/files

src/firefly/java/edu/caltech/ipac/firefly/server/rpc/VisServerCommands.java

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44
package edu.caltech.ipac.firefly.server.rpc;
55

6+
import edu.caltech.ipac.firefly.core.Util;
67
import edu.caltech.ipac.firefly.data.ServerParams;
78
import edu.caltech.ipac.firefly.server.ServCommand;
89
import edu.caltech.ipac.firefly.server.ServerCommandAccess;
@@ -18,17 +19,19 @@
1819
import edu.caltech.ipac.visualize.plot.ImagePt;
1920
import edu.caltech.ipac.visualize.plot.PixelValue;
2021
import edu.caltech.ipac.visualize.plot.plotdata.FitsExtract;
22+
import jakarta.servlet.ServletOutputStream;
23+
import jakarta.servlet.http.HttpServletRequest;
24+
import jakarta.servlet.http.HttpServletResponse;
2125
import org.json.simple.JSONArray;
2226
import org.json.simple.JSONObject;
2327

24-
import jakarta.servlet.http.HttpServletRequest;
25-
import jakarta.servlet.http.HttpServletResponse;
2628
import java.io.IOException;
2729
import java.nio.ByteBuffer;
2830
import java.nio.ByteOrder;
2931
import java.nio.channels.Channels;
3032
import java.nio.channels.WritableByteChannel;
3133
import java.util.List;
34+
import java.util.Map;
3235

3336
/**
3437
* @author Trey Roby
@@ -101,27 +104,78 @@ public static class ByteAryCmd extends ServerCommandAccess.HttpCommand {
101104

102105
public void processRequest(HttpServletRequest req, HttpServletResponse res, SrvParam sp) throws Exception {
103106

104-
PlotState state= sp.getState();
105-
boolean mask= sp.getOptionalBoolean(ServerParams.MASK_DATA,false);
106-
int maskBits= sp.getOptionalInt(ServerParams.MASK_BITS,0);
107-
int tileSize= sp.getRequiredInt(ServerParams.TILE_SIZE);
108-
String compress= sp.getOptional(ServerParams.DATA_COMPRESS, "FULL");
109-
CompressType ct;
110-
try {
111-
ct = CompressType.valueOf(compress.toUpperCase());
112-
} catch (IllegalArgumentException e) {
113-
ct= CompressType.FULL;
107+
String tileAction= sp.getRequired(ServerParams.TILE_ACTION);
108+
switch (tileAction) {
109+
case "create" -> createTiles(res, sp);
110+
case "delete" -> deleteTiles(res, sp);
111+
case "getTile" -> getTile(res, sp);
112+
default -> res.sendError(400, "tileAction parameter must be either 'create', 'delete', or 'getTile'. passed: "+tileAction);
114113
}
114+
}
115115

116-
byte[] data = VisServerOps.getByteStretchArrayWithUserLocking(state,tileSize,mask,maskBits,ct);
117-
116+
public void getTile(HttpServletResponse res, SrvParam sp) throws Exception {
117+
PlotState state= sp.getState();
118+
int tileNumber= sp.getRequiredInt("tileNumber");
119+
CompressType ct= getCompressType(sp);
120+
var band= sp.contains(ServerParams.BAND) ? Band.parse(sp.getOptional(ServerParams.BAND)) : Band.NO_BAND;
121+
122+
byte[] data = VisServerOps.getByteStretchTile(state, ct, tileNumber,band);
123+
if (data==null) {
124+
String msg= "Tile Not Found";
125+
if (!VisServerOps.hasByteStretchDataEntry(state)) msg+= ", No Byte Stretch Data";
126+
res.sendError(404, msg);
127+
return;
128+
}
129+
res.addHeader("tile-number", tileNumber+"");
118130
res.setContentType("application/octet-stream");
119131
ByteBuffer byteBuf = ByteBuffer.wrap(data);
120132
byteBuf.position(0);
121133
WritableByteChannel chan= Channels.newChannel(res.getOutputStream());
122134
chan.write(byteBuf);
123135
chan.close();
124136
}
137+
138+
139+
public void createTiles(HttpServletResponse res, SrvParam sp) throws Exception {
140+
PlotState state= sp.getState();
141+
boolean mask= sp.getOptionalBoolean(ServerParams.MASK_DATA,false);
142+
int maskBits= sp.getOptionalInt(ServerParams.MASK_BITS,0);
143+
int tileSize= sp.getRequiredInt(ServerParams.TILE_SIZE);
144+
CompressType ct= getCompressType(sp);
145+
VisServerOps.createByteStretchArrayWithUserLocking(state,tileSize,mask,maskBits,ct);
146+
JSONObject data = new JSONObject();
147+
var entry= VisServerOps.getByteStretchDataEntry(state);
148+
data.put("tileCount", entry!=null ? entry.getTotalTiles() : 0);
149+
sendJsonDataRet(res,data);
150+
}
151+
152+
public void deleteTiles(HttpServletResponse res, SrvParam sp) throws Exception {
153+
VisServerOps.deleteByteStretchData(sp.getState(),getCompressType(sp));
154+
sendJsonDataRet(res,null);
155+
}
156+
157+
private static void sendJsonDataRet(HttpServletResponse res, Map<?,?> data) throws IOException {
158+
String jsonData= makeJsonRetData(data);
159+
res.setContentType("application/json");
160+
res.setContentLength(jsonData.length());
161+
ServletOutputStream out = res.getOutputStream();
162+
out.write(jsonData.getBytes());
163+
out.close();
164+
}
165+
166+
private static String makeJsonRetData(Map<?,?> data) {
167+
JSONArray ary= new JSONArray();
168+
JSONObject map = new JSONObject();
169+
map.put("success", true);
170+
map.put("data", data!=null ? data : "none");
171+
ary.add(map);
172+
return ary.toString();
173+
}
174+
175+
private static CompressType getCompressType(SrvParam sp) {
176+
String compress= sp.getOptional(ServerParams.DATA_COMPRESS);
177+
return Util.Try.it(() -> CompressType.valueOf(compress)).getOrElse(CompressType.FULL);
178+
}
125179
}
126180

127181

src/firefly/java/edu/caltech/ipac/firefly/server/visualize/DirectStretchUtils.java

Lines changed: 31 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package edu.caltech.ipac.firefly.server.visualize;
22

3-
import edu.caltech.ipac.firefly.data.HasSizeOf;
43
import edu.caltech.ipac.firefly.visualize.Band;
54
import edu.caltech.ipac.firefly.visualize.PlotState;
65
import edu.caltech.ipac.visualize.plot.ActiveFitsReadGroup;
@@ -14,8 +13,8 @@
1413
import nom.tam.fits.Header;
1514

1615
import java.awt.Color;
17-
import java.io.Serializable;
1816
import java.util.ArrayList;
17+
import java.util.HashMap;
1918
import java.util.List;
2019
import java.util.concurrent.Callable;
2120
import java.util.concurrent.ExecutorService;
@@ -32,7 +31,7 @@
3231
*/
3332
public class DirectStretchUtils {
3433

35-
public enum CompressType {FULL, HALF, HALF_FULL, QUARTER_HALF, QUARTER_HALF_FULL}
34+
public enum CompressType {FULL, HALF, QUARTER, HALF_FULL, QUARTER_HALF, QUARTER_HALF_FULL}
3635

3736

3837
private static ExecutorService makeExecutorService () {
@@ -60,7 +59,7 @@ public static StretchDataInfo getStretchDataMask(PlotState state, ActiveFitsRead
6059
var sTileList= doTileStretch(sv,tileSize, StretchMaskTile::new,
6160
(stdef, strContainer) -> () -> strContainer.stretch(stdef, maskList, flip1d,fr.getNaxis1()) );
6261

63-
byte[] byte1d= combineArray(flip1d.length, sTileList.stream().map( st -> st.result).toList());
62+
List<byte[]> byte1d= sTileList.stream().map( st -> st.result).toList();
6463
return new StretchDataInfo(byte1d, null, null, getRangeValuesToUse(state));
6564
}
6665

@@ -81,41 +80,36 @@ private static StretchDataInfo getStretch3C(PlotState state, ActiveFitsReadGroup
8180
FitsRead fr= frGroup.getFitsRead(state.firstBand());
8281
StretchVars sv= getStretchVars(fr,tileSize, ct);
8382
RangeValues[] rvAry= getRangeValuesToUse(state);
84-
int bPos= 0;
85-
int bPosHalf=0;
86-
int bPosQuarter=0;
8783
Band[] bands= state.getBands();
8884
ThreeCComponents tComp= get3CComponents(frGroup,sv.totWidth,sv.totHeight,state);
8985
RGBIntensity rgbI= get3CRGBIntensity(state,frGroup,bands);
9086
new ArrayList<Stretch3CTile>(sv.tileLen);
9187

92-
var sTileList =doTileStretch(sv,tileSize, Stretch3CTile::new,
88+
List<Stretch3CTile> sTileList =doTileStretch(sv,tileSize, Stretch3CTile::new,
9389
(stdef,strContainer) -> () -> strContainer.stretch(stdef, rvAry, tComp.float1dAry,tComp.imHeadAry,tComp.histAry,rgbI) );
94-
int bLen= state.getBands().length;
95-
byte[] byte1d= new byte[sv.totWidth*sv.totHeight * bLen];
96-
byte[] byte1dHalf= useHalf(ct) ? new byte[dRoundUp(sv.totWidth,2) * dRoundUp(sv.totHeight,2) * bLen] : null;
97-
byte[] byte1dQuarter= useQuarter(ct) ? new byte[dRoundUp(sv.totWidth,4) * dRoundUp(sv.totHeight,4) * bLen] : null;
90+
91+
var byte1d= useFull(ct) ? new HashMap<Band,List<byte []>>() : null;
92+
var byte1dHalf= useHalf(ct) ? new HashMap<Band,List<byte []>>() : null;
93+
var byte1dQuarter=useQuarter(ct) ? new HashMap<Band,List<byte []>>() : null;
9894

9995
for (Stretch3CTile stretchTile : sTileList) {
100-
byte[][] tmpByte3CAry= stretchTile.result;
101-
for(int bandIdx=0; (bandIdx<3);bandIdx++) {
102-
if (tComp.float1dAry[bandIdx]!=null) {
103-
System.arraycopy(tmpByte3CAry[bandIdx],0,byte1d,bPos,tmpByte3CAry[bandIdx].length);
104-
bPos+=tmpByte3CAry[bandIdx].length;
105-
if (useHalf(ct)) {
106-
byte[] hDecimatedAry= stretchTile.resultHalf[bandIdx];
107-
System.arraycopy(hDecimatedAry, 0, byte1dHalf, bPosHalf, hDecimatedAry.length);
108-
bPosHalf += hDecimatedAry.length;
109-
}
110-
if (useQuarter(ct)) {
111-
byte[] qDecimatedAry= stretchTile.resultQuarter[bandIdx];
112-
System.arraycopy(qDecimatedAry, 0, byte1dQuarter, bPosQuarter , qDecimatedAry.length);
113-
bPosQuarter += qDecimatedAry.length;
114-
}
96+
for(Band band : bands) {
97+
if (useFull(ct)) {
98+
byte1d.computeIfAbsent(band, k -> new ArrayList<>())
99+
.add(stretchTile.result[band.getIdx()]);
100+
}
101+
if (useHalf(ct)) {
102+
byte1dHalf.computeIfAbsent(band, k -> new ArrayList<>())
103+
.add(stretchTile.resultHalf[band.getIdx()]);
104+
}
105+
if (useQuarter(ct)) {
106+
byte1dQuarter.computeIfAbsent(band, k -> new ArrayList<>())
107+
.add(stretchTile.resultQuarter[band.getIdx()]);
115108
}
116109
}
117110
}
118-
return new StretchDataInfo(byte1d, byte1dHalf, byte1dQuarter, rvAry);
111+
112+
return new StretchDataInfo(byte1d,byte1dHalf,byte1dQuarter, rvAry);
119113
}
120114

121115
private static <T> List<T> doTileStretch(StretchVars sv, int tileSize, Callable<T> stretchContainerFactory,
@@ -137,21 +131,14 @@ private static <T> List<T> doTileStretch(StretchVars sv, int tileSize, Callable<
137131
}
138132

139133
private static StretchDataInfo buildStandardResult(List<StretchStandardTile> sTileList, RangeValues rv,
140-
int totWidth, int totHeight, CompressType ct) throws Exception {
141-
var taskList= new ArrayList<Callable<Void>>();
142-
byte[] byte1d= useFull(ct ) ? new byte[totWidth*totHeight] : null;
143-
byte[] byte1dQuarter= useQuarter(ct) ? new byte[dRoundUp(totWidth,4) * dRoundUp(totHeight,4)]:null;
144-
byte[] byte1dHalf= useHalf(ct) ? new byte[dRoundUp(totWidth,2) * dRoundUp(totHeight,2)]:null;
145-
if (useFull(ct)) {
146-
taskList.add(() -> combineArray(byte1d, sTileList.stream().map( st -> st.result).toList()));
147-
}
148-
if (useQuarter(ct)) {
149-
taskList.add(() -> combineArray(byte1dQuarter, sTileList.stream().map( st -> st.resultQuarter).toList()));
150-
}
151-
if (useHalf(ct)) {
152-
taskList.add(() -> combineArray(byte1dHalf, sTileList.stream().map( st -> st.resultHalf).toList()));
153-
}
154-
invokeList(taskList);
134+
int totWidth, int totHeight, CompressType ct) throws Exception {
135+
List<byte[]> byte1d= useFull(ct) ?
136+
sTileList.stream().map( st -> st.result).toList() : null;
137+
List<byte[]> byte1dHalf= useHalf(ct) ?
138+
sTileList.stream().map( st -> st.resultHalf).toList() : null;
139+
List<byte[]> byte1dQuarter= useQuarter(ct) ?
140+
sTileList.stream().map( st -> st.resultQuarter).toList() : null;
141+
155142
return new StretchDataInfo(byte1d, byte1dHalf, byte1dQuarter, new RangeValues[] {rv});
156143
}
157144

@@ -179,18 +166,6 @@ private static boolean useFull(CompressType ct) {
179166
return ct==CompressType.FULL || ct==CompressType.HALF_FULL || ct==CompressType.QUARTER_HALF_FULL;
180167
}
181168

182-
private static float [] flipFloatArray(float [] float1d, int naxis1, int naxis2) {
183-
float [] flipped= new float[float1d.length];
184-
int idx=0;
185-
for (int y= naxis2-1; y>=0; y--) {
186-
for (int x= 0; x<naxis1; x++) {
187-
flipped[idx]= float1d[y*naxis1+x];
188-
idx++;
189-
}
190-
}
191-
return flipped;
192-
}
193-
194169
private static StretchVars getStretchVars(FitsRead fr, int tileSize, CompressType ct) {
195170
int totWidth= fr.getNaxis1();
196171
int totHeight= fr.getNaxis2();
@@ -240,21 +215,6 @@ private static RangeValues[] getRangeValuesToUse(PlotState state) {
240215
return new RangeValues[] { state.getRangeValues(RED), state.getRangeValues(GREEN), state.getRangeValues(BLUE) };
241216
}
242217

243-
private static Void combineArray(byte[] target, List<byte[]> aList) {
244-
int pos= 0;
245-
for (var dAry : aList) {
246-
System.arraycopy(dAry, 0, target, pos , dAry.length);
247-
pos += dAry.length;
248-
}
249-
return null;
250-
}
251-
252-
private static byte[] combineArray(int length, List<byte[]> aList) {
253-
byte[] target= new byte[length];
254-
combineArray(target,aList);
255-
return target;
256-
}
257-
258218
private static int dRoundUp(int v, int factor) { return v % factor == 0 ? v/factor : v/factor +1; }
259219

260220
private static byte[] makeDecimated(byte[] in, int factor, int width, int height) {
@@ -312,70 +272,7 @@ private record ThreeCComponents(float[][] float1dAry, ImageHeader[] imHeadAry, H
312272
private record StretchVars(int totWidth, int totHeight, int xPanels, int yPanels, int tileLen, CompressType ct) {}
313273
private record StretchTileDef(int x, int y, int width, int height, CompressType ct) {}
314274

315-
public static class StretchDataInfo implements Serializable, HasSizeOf {
316-
private final byte [] byte1d;
317-
private final byte [] byte1dHalf;
318-
private final byte [] byte1dQuarter;
319-
private final RangeValues[] rvAry;
320-
321-
public StretchDataInfo(byte[] byte1d, byte[] byte1dHalf, byte[] byte1dQuarter, RangeValues[] rvAry) {
322-
this.byte1d = byte1d;
323-
this.byte1dHalf = byte1dHalf;
324-
this.byte1dQuarter = byte1dQuarter;
325-
this.rvAry= rvAry;
326-
}
327-
328-
public byte[] findMostCompressAry(CompressType ct) {
329-
return switch (ct) {
330-
case FULL -> byte1d;
331-
case QUARTER_HALF_FULL, QUARTER_HALF -> byte1dQuarter;
332-
case HALF, HALF_FULL -> byte1dHalf;
333-
};
334-
}
335-
336-
public static String getMostCompressedDescription(CompressType ct) {
337-
return switch (ct) {
338-
case FULL -> "Full";
339-
case QUARTER_HALF_FULL, QUARTER_HALF -> "Quarter";
340-
case HALF, HALF_FULL -> "Half";
341-
};
342-
}
343-
344-
public boolean isRangeValuesMatching(PlotState state) {
345-
if (!state.isThreeColor()) {
346-
return rvAry.length==1 && rvAry[0].toString().equals(state.getRangeValues().toString());
347-
}
348-
for (Band band : new Band[]{RED, GREEN, BLUE}) {
349-
if (state.isBandUsed(band)) {
350-
int idx= band.getIdx();
351-
if (rvAry[idx]==null || !rvAry[idx].toString().equals(state.getRangeValues(band).toString())) {
352-
return false;
353-
}
354-
}
355-
}
356-
return true;
357-
}
358-
359-
/**
360-
* create a version of the object with only the full byte array and optionally the half if the
361-
* CompressType is only using the quarter
362-
* @return a version of StretchDataInfo without all the data we will not use again
363-
*/
364-
public StretchDataInfo copyParts(CompressType ct) {
365-
boolean keepHalf= ct== CompressType.QUARTER_HALF_FULL || ct== CompressType.QUARTER_HALF;
366-
return new StretchDataInfo(byte1d, keepHalf?byte1dHalf:null, null, rvAry);
367-
}
368-
369-
@Override
370-
public long getSizeOf() {
371-
long sum= rvAry.length * 80L;
372-
if (byte1d!=null) sum+=byte1d.length;
373-
if (byte1dHalf!=null) sum+=byte1dHalf.length;
374-
if (byte1dQuarter!=null) sum+=byte1dQuarter.length;
375-
return sum;
376-
}
377-
}
378-
private static class Stretch3CTile {
275+
private static class Stretch3CTile {
379276
byte[][] result;
380277
byte[][] resultHalf;
381278
byte[][] resultQuarter;

0 commit comments

Comments
 (0)