improve loading speed
2 changed files+66−28
web-ui/src/components/dataset/TimeseriesView.tsxmodified+22−4View file
@@ -219,10 +219,9 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
219219 // Calculate yRange from data
220220 const yRange = useMemo<Range>(() => {
221221 if (!dataY) return { min: 0, max: 1 };
222- const values = Array.from(dataY);
223222 return {
224- min: Math.min(...values),
225- max: Math.max(...values),
223+ min: computeMin(dataY),
224+ max: computeMax(dataY),
226225 };
227226 }, [dataY]);
228227
@@ -242,7 +241,6 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
242241 xRange,
243242 yRange,
244243 };
245- console.log("--- posting message to worker", msg);
246244 worker.postMessage(msg);
247245 }, [width, height, dataT, dataY, worker, margins, xRange, yRange]);
248246
@@ -404,4 +402,24 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
404402 );
405403 };
406404
405+const computeMin = (data: SupportedTypedArray) => {
406+ let min = Infinity;
407+ for (let i = 0; i < data.length; i++) {
408+ if (data[i] < min) {
409+ min = data[i];
410+ }
411+ }
412+ return min;
413+};
414+
415+const computeMax = (data: SupportedTypedArray) => {
416+ let max = -Infinity;
417+ for (let i = 0; i < data.length; i++) {
418+ if (data[i] > max) {
419+ max = data[i];
420+ }
421+ }
422+ return max;
423+};
424+
407425 export default TimeseriesView;
web-ui/src/hooks/TimeseriesDataClient.tsmodified+44−24View file
@@ -24,6 +24,8 @@ export class TimeseriesDataClient {
2424 private dtype: DType | null = null;
2525 private chunkSize: number;
2626 private cache: ChunkCache = {};
27+ private inProgressFetches: { [key: number]: Promise<SupportedTypedArray> } =
28+ {};
2729 private datasetJsonUrl: string;
2830 private datasetDataUrl: string;
2931
@@ -81,36 +83,54 @@ export class TimeseriesDataClient {
8183 }
8284
8385 private async fetchChunk(chunkIndex: number): Promise<SupportedTypedArray> {
86+ // Return cached chunk if available
8487 if (this.cache[chunkIndex]) {
8588 return this.cache[chunkIndex];
8689 }
8790
88- if (!this.dtype) {
89- throw new Error("Data type not initialized");
91+ // If this chunk is already being fetched, wait for it to complete
92+ const inProgressFetch = this.inProgressFetches[chunkIndex];
93+ if (inProgressFetch !== undefined) {
94+ return inProgressFetch;
9095 }
9196
92- const start = chunkIndex * this.chunkSize;
93- const end = Math.min(start + this.chunkSize, this.shape);
94- const url = this.datasetDataUrl;
95- const itemSize = TypedArrayConstructors[this.dtype].BYTES_PER_ELEMENT;
96- const byteStart = start * itemSize;
97- const byteEnd = end * itemSize;
98-
99- const response = await fetch(url, {
100- headers: {
101- Range: `bytes=${byteStart}-${byteEnd - 1}`,
102- },
103- });
104- if (!response.ok) {
105- throw new Error(`Failed to fetch chunk: ${response.statusText}`);
106- }
107-
108- const buffer = await response.arrayBuffer();
109- const ArrayConstructor = TypedArrayConstructors[this.dtype];
110- const data = new ArrayConstructor(buffer);
111- this.cache[chunkIndex] = data;
112-
113- return data;
97+ // Start new fetch and track it
98+ const fetchPromise = (async () => {
99+ if (!this.dtype) {
100+ throw new Error("Data type not initialized");
101+ }
102+
103+ const start = chunkIndex * this.chunkSize;
104+ const end = Math.min(start + this.chunkSize, this.shape);
105+ const url = this.datasetDataUrl;
106+ const itemSize = TypedArrayConstructors[this.dtype].BYTES_PER_ELEMENT;
107+ const byteStart = start * itemSize;
108+ const byteEnd = end * itemSize;
109+
110+ try {
111+ const response = await fetch(url, {
112+ headers: {
113+ Range: `bytes=${byteStart}-${byteEnd - 1}`,
114+ },
115+ });
116+ if (!response.ok) {
117+ throw new Error(`Failed to fetch chunk: ${response.statusText}`);
118+ }
119+
120+ const buffer = await response.arrayBuffer();
121+ const ArrayConstructor = TypedArrayConstructors[this.dtype];
122+ const data = new ArrayConstructor(buffer);
123+ this.cache[chunkIndex] = data;
124+ return data;
125+ } finally {
126+ // Clean up the in-progress fetch regardless of success/failure
127+ delete this.inProgressFetches[chunkIndex];
128+ }
129+ })();
130+
131+ // Store the promise for other requests to wait on
132+ this.inProgressFetches[chunkIndex] = fetchPromise;
133+ return fetchPromise;
114134 }
115135
116136 async fetchRange(start: number, end: number): Promise<SupportedTypedArray> {