Closed applmak closed 2 years ago
I cannot reproduce this on OSX by just doing a simple fetch
burst:
let count = 0;
while(true) {
const start = performance.now();
const r = await fetch("http://localhost:9000/img.png");
await r.arrayBuffer();
const end = performance.now() - start;
console.log(`fetch ${count} took ${end}ms`);
count++;
}
So I think the slowdown is related to:
passes the resulting ArrayBuffer to a pool of WebWorkers using postMessage
Hmm cannot reproduce with this too:
const source = `
let b = []
onmessage = function(e, a) {
b.push(e.data);
}
`
const worker = new Worker(URL.createObjectURL(new Blob([source], {type: 'application/javascript'})), { type: 'module' });
let count = 0;
while(true) {
const start = performance.now();
const r = await fetch("http://localhost:9000/img.png");
const buf = await r.arrayBuffer();
worker.postMessage(buf, [buf]);
const end = performance.now() - start;
console.log(`fetch ${count} took ${end}ms`);
count++;
}
Here's some more code. I'll work to create a tinier test case when I am able to:
const downloadImageTransformer = new WritableStream<LibraryItem>({
async write(item) {
const baseUrl = `${item.baseUrl}=w32-h32-d`;
let formattedBytes;
try {
console.time("image fetch");
formattedBytes = await (await fetch(baseUrl)).arrayBuffer();
console.timeEnd("image fetch");
} catch (e) {
return;
}
const worker = workers[nextWorker = (nextWorker + 1) % workers.length];
worker.postMessage({ imageData: formattedBytes, id: item.id }, [
formattedBytes,
]);
},
}, {
highWaterMark: 500,
});
And then the worker:
import { Image } from "https://deno.land/x/imagescript/mod.ts";
interface ImageMessage {
id: string;
imageData: ArrayBuffer;
}
async function reportHash(id, image: Image) {
const hashBuffer = await crypto.subtle.digest("SHA-1", image.bitmap);
const hash = Array.from(new Uint8Array(hashBuffer)).map((b) =>
b.toString(16).padStart(2, "0")
).join("");
self.postMessage({ id, hash });
}
self.onmessage = async (evt) => {
const { imageData, id } = evt.data as ImageMessage;
let image;
try {
image = await Image.decode(imageData);
} catch (e) {
console.error(e);
return;
}
await reportHash(id, image);
image.rotate(90);
await reportHash(id, image);
image.rotate(90);
await reportHash(id, image);
image.rotate(90);
await reportHash(id, image);
};
self.postMessage("ready");
Okay, when I remove the Worker, I see the same slowdown.
When I remove the WritableStream and just do the fetch
in the ReadableStream, I no longer see the slowdown.
And now, when I added the WritableStream back in, I no longer see the slowdown.
Given that this works, it's probably not worth spending time chasing this down much more. If I get this to repro again, I'll reopen the bug. Thanks for spending time on it!
Hi! I'm trying to download my whole Google Photos library using its API. I'm also trying to use the fancy new Readable/WritableStream classes to do so, because they look like fun. My code looks roughly like this:
And then there's a writablestream that actually downloads each
LibraryItem
as a 32px x 32px thumbnail and passes the resultingArrayBuffer
to a pool of WebWorkers usingpostMessage
, which do some post-processing on them.Anyway, I noticed that the actual downloading of the images seemed to slow down over time. At first, I assumed some kind of rate-limiting from Google's image servers was the culprit, but a restart of the program resumes downloading the images at normal speed, which wouldn't make sense given a normal OAuth-token-based rate-limiting strategy. So, I took some data:
And made a spreadsheet with the results. These data show that at the 385th fetch, the latency of the fetch operation increases significantly. I've left the program running for a day, and these fetches continue to slow down significantly, eventually taking well over a minute to return a small 32x32 thumbnail of the image.
Obviously, this slow latency means that I don't think I'll ever be able to download my whole library!
To help to figure out what's going on, I use the chrome inspector to take a profile. The resulting flamegraph showed small bursts of JS with almost no activity (except for
program
) in between. At the time I took the profile, these pauses were 6 seconds, but as I've said before, these continued to increase over time.In order to see if it was v8 behaving badly, I built a recent-ish version of v8 in release mode, ran the program, and took a long profile. The results aren't particularly illuminating to me, but perhaps they are to you? Here are the C++ parts of the isolates (generated via
for i in $(ls isolate-*.log); do echo $i; D8_PATH=v8/v8/out.gn/arm64.release ./v8/v8/tools/mac-tick-processor $i 2>/dev/null | grep -A 11 '\[C++\]'; done
). They are totally dominated by the private symbol_open_dprotected_np
, which I know nothing about.Any hints from your end?