Noh | エンジニア向け情報共有コミュニティ
Signup / Login

Cloud Functions for Firebase のコールドスタート含む時間計測したら思ったより高速化されていた

firebase

投稿日: 2024/05/23

更新日: 2024/05/28

Firebase Cloud Functions を使ってAPIを開発したり、動的Webサイトのホスティングを行おうとしたときに気になるのはコールドスタートにかかる時間だと思います。

個人的に動的Webサイトのホスティング先にコールドスタートのないCloudflare Workersを検討していたのですが、Node環境でないので未対応のPackageが多く、苦戦していました。また、個人的にFirebaseやGCPを中心に活用しているので、Cloud Functionsについて改めて調査してみました。

事前情報

2022/02 ごろの記事ですが、メモリを増やすとFirestoreのコネクションを張る時間が軽減されるらしい。コールドスタート含めて1.3秒 ~ 4.5秒ほどかかる。

今回、Cloud Functionsの関数内で、Firestoreとのやりとりがあるが、Firestoreへの最初のリクエスト時には、新規にコネクションを貼る処理が発生する。Cloud Functionsの新しいインスタンスが立ち上がるたびに、Firestoreとの新規コネクション生成が発生するので、その分さらに時間がかかる。
https://zenn.dev/seisei/articles/7c02fdffa866a0

色々と調査した結果、コネクションの確立を早くするためには、メモリの割り当てを増やすといいとのこと。当初、メモリの割り当てはデフォルトの256MBで、コールドスタート時の実行時間は4.5秒ほどかかっていた。メモリ使用量を見ても、それほど問題に思えなかったので、それに紐づいているCPUの割り当てが原因だと思う。メモリの割り当て量を512MBに増やしたところ、コールドスタート時の実行時間は2.2秒となった。さらに、1024MBに増やすと、1.3秒にまで改善した。
https://zenn.dev/seisei/articles/7c02fdffa866a0

さらに上記記事が参考にしているのは2019年の記事であり、かなり時間がたっている。

https://techlife.cookpad.com/entry/2019/07/05/100000

また、2020/09ごろにCloud Run および Cloud Functions(第 2 世代)向けに起動時CPU ブースト機能が提供されている。

Firebase Admin SDK なし

Firebase Admin SDK を使わないコードでレスポンスが返ってくる時間を測ります。適当な計測なのであくまで参考程度にしてください。

コードは以下の感じです。

import * as functionsV1 from "firebase-functions/v1"; import { onRequest } from "firebase-functions/v2/https"; // import * as logger from "firebase-functions/logger"; // Start writing functions // https://firebase.google.com/docs/functions/typescript export const v1m128 = functionsV1 .region("asia-northeast1") .runWith({ maxInstances: 1, timeoutSeconds: 10, memory: "128MB", }) .https.onRequest((req, res) => { res.json({}); }); export const v1m256 = functionsV1 .region("asia-northeast1") .runWith({ maxInstances: 1, timeoutSeconds: 10, memory: "256MB", }) .https.onRequest((req, res) => { res.json({}); }); export const v1m512 = functionsV1 .region("asia-northeast1") .runWith({ maxInstances: 1, timeoutSeconds: 10, memory: "512MB", }) .https.onRequest((req, res) => { res.json({}); }); export const v2m128 = onRequest( { region: ["asia-northeast1"], maxInstances: 1, timeoutSeconds: 10, memory: "128MiB", }, (req, res) => { res.status(200).send({}); } ); export const v2m256 = onRequest( { region: ["asia-northeast1"], maxInstances: 1, timeoutSeconds: 10, memory: "256MiB", }, (req, res) => { res.status(200).send({}); } ); export const v2m512 = onRequest( { region: ["asia-northeast1"], maxInstances: 1, timeoutSeconds: 10, memory: "512MiB", }, (req, res) => { res.status(200).send({}); } );

結果です。

functions version / memory1回目2回目
v1 / 128MB1270ms1320ms
v1 / 256MB1170ms1160ms
v1 / 512MB1250ms960ms
v2 / 128MB1600ms1330ms
v2 / 256MB1280ms1500ms
v2 / 512MB1430ms1390ms

v1とv2共にメモリが128MBの時点で予想より速いです。この結果はコールドスタートの時間を含んでいます。また、メモリを増やしても高速化はしてないように見えます。

Firebase Admin SDK あり(データFetchなし)

コードは先ほどのFirebase Admin SDK なしのコードとほぼ同じです。import { onRequest } from "firebase-functions/v2/https";の下に以下のコードを追加してFirebase Admin SDKを初期化します。

import * as admin from "firebase-admin"; admin.initializeApp();

結果です。

functions version / memory1回目2回目
v1 / 128MB1830ms1730ms
v1 / 256MB1730ms1720ms
v1 / 512MB1420ms1800ms
v2 / 128MB1640ms1770ms
v2 / 256MB2120ms2140ms
v2 / 512MB1380ms1620ms

初期化分遅くなりましたが、それもわずかな差です。
参考にした記事のように4秒以上かかることもありませんでした。

Firebase Admin SDK あり(データFetchあり)

Firebase Admin SDKを初期化のあとに、FirestoreからデータをFetchしてみます。

import * as functionsV1 from "firebase-functions/v1"; import { onRequest } from "firebase-functions/v2/https"; // import * as logger from "firebase-functions/logger"; // Start writing functions // https://firebase.google.com/docs/functions/typescript import * as admin from "firebase-admin"; admin.initializeApp(); export const v1m128 = functionsV1 .region("asia-northeast1") .runWith({ maxInstances: 1, timeoutSeconds: 10, memory: "128MB", }) .https.onRequest(async (req, res) => { const adminDb = admin.firestore(); const ref = adminDb.collection("dummy").doc("dummy"); await ref.get(); res.status(200).send({}); }); export const v1m256 = functionsV1 .region("asia-northeast1") .runWith({ maxInstances: 1, timeoutSeconds: 10, memory: "256MB", }) .https.onRequest(async (req, res) => { const adminDb = admin.firestore(); const ref = adminDb.collection("dummy").doc("dummy"); await ref.get(); res.status(200).send({}); }); export const v1m512 = functionsV1 .region("asia-northeast1") .runWith({ maxInstances: 1, timeoutSeconds: 10, memory: "512MB", }) .https.onRequest(async (req, res) => { const adminDb = admin.firestore(); const ref = adminDb.collection("dummy").doc("dummy"); await ref.get(); res.status(200).send({}); }); export const v2m128 = onRequest( { region: ["asia-northeast1"], maxInstances: 1, timeoutSeconds: 10, memory: "128MiB", }, async (req, res) => { const adminDb = admin.firestore(); const ref = adminDb.collection("dummy").doc("dummy"); await ref.get(); res.status(200).send({}); } ); export const v2m256 = onRequest( { region: ["asia-northeast1"], maxInstances: 1, timeoutSeconds: 10, memory: "256MiB", }, async (req, res) => { const adminDb = admin.firestore(); const ref = adminDb.collection("dummy").doc("dummy"); await ref.get(); res.status(200).send({}); } ); export const v2m512 = onRequest( { region: ["asia-northeast1"], maxInstances: 1, timeoutSeconds: 10, memory: "512MiB", }, async (req, res) => { const adminDb = admin.firestore(); const ref = adminDb.collection("dummy").doc("dummy"); await ref.get(); res.status(200).send({}); } );

結果です。

functions version / memory1回目2回目
v1 / 128MB2470ms2070ms
v1 / 256MB2120ms3970ms
v1 / 512MB2800ms1900ms
v2 / 128MB2390ms2490ms
v2 / 256MB2310ms1150ms
v2 / 512MB2500ms1850ms

増えた時間は純粋にFetchにかかった時間という感じですね。実際のAPIとして使うときに高速ではないですが、実用上許容範囲だと思います。

まとめ

Cloud Functionsのコールドスタートは十分に速かったです。Firebase Admin SDKの初期化なしの場合1000 ~ 1500ms程度、Firebase Admin SDKの初期化ありの場合1500 ~ 2000ms程度でした。
ちなみにコールドスタートなしの場合、15 ~ 100ms程度になります。

計測方法が全く違うので単純に比較できませんが、2022年ごろの記事のように4秒以上かかるといったことはありませんでした。また、メモリを増やしてもFirebase Admin SDKの初期化にかかる時間は短くなりませんでした。メモリを1GBも計測しましたが大差ありませんでした。
v1とv2の性能差が出なかったのは意外でした。しかし、v2の方がコールドスタートしづらいように改善されてます。
2024年9月ごろにv2がデフォルトになるというメールが来ていたのですが、2024年5月時点でCloud Functions for Firebase(第 2 世代)はまだ現在は公開プレビューなんですよね。。。

Firebase Admin SDKの初期化には500ms程度かかっていますが、これはそこまで気にしなくてもいいです。なぜなら2回目以降のアクセス(コールドスタートなしの場合)で同じインスタンスを使う場合はFirebase Admin SDKの初期化はスキップされるからです。

コールドスタート含んでこの時間は個人的に想像より早かったですし、実用的にも許容範囲でした。GCPで運用しているサービスではCloud Functionsを一層活用しようと思ったのでした。めでたし、めでたし。

yosi

Noh を作ってるエンジニア。

目次