Skip to content

Commit 64fb14b

Browse files
Merge pull request #7 from lightpanda-io/playwright
playwright: add playwright bench for chrome
2 parents f17645a + 8124438 commit 64fb14b

File tree

6 files changed

+245
-9
lines changed

6 files changed

+245
-9
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
/ws/ws
2+
/node_modules

README.md

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,18 @@ By default it exposes the `public` dir using the `1234` port.
5353
$ go run ws/main.go
5454
```
5555

56-
## Single request
57-
58-
This bench is a very basic test to compare the two software.
59-
We start the browser and request the fake web page once with full JS execution. The final DOMTree is
60-
rendered in stdout.
61-
6256
### Test machine
6357

6458
The tests are run in an AWS m5.large (x86_64) with a fresh Debian install.
6559

6660
![aws.m5 neofetch](./img/aws_m5_neofetch.png)
6761

62+
## Single request
63+
64+
This bench is a very basic test to compare the two software.
65+
We start the browser and request the fake web page once with full JS execution. The final DOMTree is
66+
rendered in stdout.
67+
6868
We use Google Chrome version 122.0.6261.94.
6969

7070
```console
@@ -147,7 +147,46 @@ $ /usr/bin/time -v ./browsercore-get --dump http://127.0.0.1:1234/campfire-comme
147147
Exit status: 0
148148
```
149149

150-
## Multiple requests
150+
## Multiple requests using Playwright
151+
152+
We compare now multiple page loads and js evaluations using
153+
[Playwright](https://playwright.dev).
154+
155+
### Dependencies
156+
157+
To run the benchmark, you need ti install [nodejs](https://nodejs.org/en/download).
158+
159+
Once `nodejs` is installed, please run a `npm install` to install nodejs
160+
dependencies, mainly Playwright.
161+
162+
You have also to install [Google Chrome](https://www.google.com/chrome/) and
163+
Lightpanda browser, but the code is not publicly available yet.
164+
165+
### Google Chrome benchmark
166+
167+
We use Google Chrome version 123.0.6312.105.
168+
169+
The `playwright/chrome.js` benchmark accepts multiple env vars to be configured.
170+
* `CHROME_PATH` is the path to your Google Chrome bin,
171+
* `BASE_URL` is the base url of the running web reser to request, by default `http://127.0.0.1:1234`,
172+
* `RUNS` is the number of pages loaded by the benchmark, default is `100`.
173+
174+
`npm run bench-chrome` starts a playwright process, load a Google Chrome
175+
instance and load the page to extract data 100 times.
176+
177+
```console
178+
$ CHROME_PATH=`which google-chrome` npm run bench-chrome
179+
180+
> [email protected] bench-chrome
181+
> node playwright/chrome.js
182+
183+
................................................................................
184+
....................
185+
total runs 100
186+
total duration (ms) 18792
187+
avg run duration (ms) 184
188+
min run duration (ms) 168
189+
max run duration (ms) 323
190+
```
151191

152-
We plan to create a benchmark to compare the memory used during multiple
153-
successive requests sent to a CDP server.
192+
![aws.m5 Playwright with Google Chrome](./img/aws_m5_playwright_chrome.png)

img/aws_m5_playwright_chrome.png

144 KB
Loading

package-lock.json

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"type": "module",
3+
"name": "demo",
4+
"version": "1.0.0",
5+
"description": "Lightpanda browser demo",
6+
"main": "index.js",
7+
"scripts": {
8+
"install-chrome": "npx playwright install chrome",
9+
"ws": "go run ws/main.go",
10+
"bench-chrome": "node playwright/chrome.js"
11+
},
12+
"repository": {
13+
"type": "git",
14+
"url": "git+https://github.com/lightpanda-io/demo.git"
15+
},
16+
"keywords": [],
17+
"author": "Lightpanda",
18+
"license": "Apache 2",
19+
"bugs": {
20+
"url": "https://github.com/lightpanda-io/demo/issues"
21+
},
22+
"homepage": "https://lightpanda.io",
23+
"dependencies": {
24+
"playwright": "^1.42.1"
25+
}
26+
}

playwright/chrome.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Import the Chromium browser into our scraper.
2+
import { chromium } from 'playwright';
3+
4+
// options passed to the browser.
5+
let browser_options = {};
6+
7+
// chrome browser path
8+
if (process.env.CHROME_PATH) {
9+
browser_options.executablePath = process.env.CHROME_PATH;
10+
}
11+
12+
// headless
13+
if (process.env.HEADLESS) {
14+
browser_options.headless = process.env.HEADLESS === 'true';
15+
}
16+
17+
// web serveur url
18+
const baseURL = process.env.BASE_URL ? process.env.BASE_URL : 'http://127.0.0.1:1234';
19+
20+
// runs
21+
const runs = process.env.RUNS ? parseInt(process.env.RUNS) : 100;
22+
23+
// measure general time.
24+
const gstart = process.hrtime.bigint();
25+
// store all run durations
26+
let metrics = [];
27+
28+
// Open a Chromium browser. We use headless: false
29+
// to be able to watch the browser window.
30+
const browser = await chromium.launch(browser_options);
31+
32+
for (var run = 1; run<=runs; run++) {
33+
34+
// measure run time.
35+
const rstart = process.hrtime.bigint();
36+
37+
const context = await browser.newContext({
38+
baseURL: baseURL,
39+
});
40+
41+
const page = await context.newPage();
42+
await page.goto('/campfire-commerce');
43+
44+
// ensure the price is loaded.
45+
await page.waitForFunction(() => {
46+
const price = document.querySelector('#product-price');
47+
return price.textContent.length > 0;
48+
});
49+
50+
51+
// ensure the reviews are loaded.
52+
await page.waitForFunction(() => {
53+
const reviews = document.querySelectorAll('#product-reviews > div');
54+
return reviews.length > 0;
55+
});
56+
57+
let res = {};
58+
59+
res.name = await page.locator('#product-name').textContent();
60+
res.price = parseFloat((await page.locator('#product-price').textContent()).substring(1));
61+
res.description = await page.locator('#product-description').textContent();
62+
res.features = await page.locator('#product-features > li').allTextContents();
63+
res.image = await page.locator('#product-image').getAttribute('src');
64+
65+
let related = [];
66+
var i = 0;
67+
for (const row of await page.locator('#product-related > div').all()) {
68+
related[i++] = {
69+
name: await row.locator('h4').textContent(),
70+
price: parseFloat((await row.locator('p').textContent()).substring(1)),
71+
image: await row.locator('img').getAttribute('src'),
72+
};
73+
}
74+
res.related = related;
75+
76+
let reviews = [];
77+
var i =0;
78+
for (const row of await page.locator('#product-reviews > div').all()) {
79+
reviews[i++] = {
80+
title: await row.locator('h4').textContent(),
81+
text: await row.locator('p').textContent(),
82+
};
83+
}
84+
res.reviews = reviews;
85+
86+
// console.log(res);
87+
88+
process.stderr.write('.');
89+
if(run % 80 == 0) process.stderr.write('\n');
90+
91+
await page.close();
92+
await context.close();
93+
94+
metrics[run] = process.hrtime.bigint() - rstart;
95+
}
96+
97+
// Turn off the browser to clean up after ourselves.
98+
await browser.close();
99+
100+
const gduration = process.hrtime.bigint() - gstart;
101+
102+
process.stderr.write('\n');
103+
104+
const avg = metrics.reduce((s, a) => s += a) / BigInt(metrics.length);
105+
const min = metrics.reduce((s, a) => a < s ? a : s);
106+
const max = metrics.reduce((s, a) => a > s ? a : s);
107+
108+
console.log('total runs', runs);
109+
console.log('total duration (ms)', (gduration/1000000n).toString());
110+
console.log('avg run duration (ms)', (avg/1000000n).toString());
111+
console.log('min run duration (ms)', (min/1000000n).toString());
112+
console.log('max run duration (ms)', (max/1000000n).toString());
113+

0 commit comments

Comments
 (0)