A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/vercel/satori/issues/590 below:

2x faster with SatoriOptions.fonts as a global variable · Issue #590 · vercel/satori · GitHub

Feature Request Description

Hello everyone,

I am currently running a next.js standalone application in a production environment. Thanks to your hard work, we have been able to generate stunning OG images, greatly encouraging users to share more on social media. I am truly grateful for this.

However, serving the standalone application has introduced some constraints related to the edge runtime. As a result, we have been utilizing satori and sharp in the node.js runtime instead of relying on edge runtime and next/og.

The performance issue has been a concern, though. Combining several React components and 2-3 fonts has resulted in more delay than anticipated. Executing the following wrk command on a MacBook M1 Pro showed a performance of 10-15 RPS. The actual production environment, with less CPU resources, showed about 5 RPS:

$ wrk -t10 -c 30 -d30s http://localhost:3000/og/review\?updatedAt\=2024-01-29T14:30:22
Running 30s test @ http://localhost:3000/og/review\?updatedAt=2024-01-29T14:30:22
  10 threads and 30 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.53s   339.74ms   2.00s    66.77%
    Req/Sec     2.18      2.99    20.00     91.64%
  480 requests in 30.10s, 171.63MB read
  Socket errors: connect 0, read 0, write 0, timeout 149
Requests/sec:     15.95
Transfer/sec:      5.70MB

Adding the NODE_OPTIONS=--inspect option for profiling revealed that the addFonts() function, called during the satori constructor execution, was being repeatedly executed. (We have conducted profiling using Chrome DevTools as shown below.)

[1] Bottom-Up without grouping

[2] Chrome DevTools - Performance tap

Tracing results suggest that the bottleneck mostly occurs when calling the addFonts() function from the satori() constructor. While the actual image generation takes only about 20-50ms, 25ms is spent on addFonts(). Despite having the font buffer declared as a global variable, unnecessary operations are executed during each satori constructor call due to the processing of opentype fonts. It appears there's no interface for injecting the results of addFonts(), thus leading to the repeated execution of this function.

Referencing src/fonts.ts #L135, as shown below:

  public addFonts(fontOptions: FontOptions[]) {
    // ...
    const font = opentype.parse(
        // Buffer to ArrayBuffer.
        'buffer' in data
          ? data.buffer.slice(
              data.byteOffset,
              data.byteOffset + data.byteLength
            )
          : data,
        // @ts-ignore
        { lowMemory: true }
      )
    // ...
    this.fonts.get(_name).push([font, fontOption.weight, fontOption.style])
    // ...
  }

It seems each execution of satori() assigns the result of opentype.parse() to an internal variable. I'm curious if there's a way to declare the this.fonts variable globally and either inject it from outside or reuse it in some manner.

Since we consistently use 2-3 specific fonts, minimizing the number of addFonts() calls is expected to improve throughput.

This is our /pages/api/og/[id].tsx .

const [boldFontFile, semiBoldFontFile, ratingStar, inflearnLogo] =
  await Promise.all([
    getFontData("bold"),
    getFontData("semiBold"),
    readFile(path.resolve("public/images/rating_star.png")),
    readFile(path.resolve("public/images/logo.png")),
  ]);

export default async function handler(
  request: NextApiRequest,
  response: NextApiResponse
) {
  try {
    const svg = await satori(JSX,
      {
        width: 1200,
        height: 630,
        embedFont: true,
        fonts: [
          {
            name: "Pretendard",
            data: boldFontFile,
            weight: 700,
            style: "normal",
          },
          {
            name: "Pretendard",
            data: semiBoldFontFile,
            weight: 600,
            style: "normal",
          },
        ],
      }
    );

I apologize for my bad English and hope my message is clear. Please feel free to point out any imperfections in my ideas or offer any assistance you can. Thank you for taking the time to read this.

Additional Context

Maybe profiles image can be additional context.

jojoldu2inflab, LeeMoonki, shinyuna, rokinflearn, rlaisqlsinflab and 21 more


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4