FPS (Frames Per Second) — είναι μια μετρική που δείχνει πόσα μεμονωμένα καρέ αποδίδει το γραφικό σύστημα σε ένα δευτερόλεπτο. Στην κινητή ανάπτυξη, το FPS είναι ένας τυπικός δείκτης απόδοσης του UI: όσο υψηλότερο είναι το FPS, τόσο πιο ομαλές είναι οι κινούμενες εικόνες και τόσο πιο αποκριτική η διεπαφή. Σύμφωνα με τα δεδομένα του Google Android Performance, 2025, η τιμή-στόχος του FPS για εφαρμογές κινητού είναι 60 καρέ ανά δευτερόλεπτο — αυτό είναι το όριο στο οποίο το ανθρώπινο μάτι αντιλαμβάνεται την κίνηση ως συνεχή και ομαλή.
Βασικά σημεία
FPS (Frames Per Second) — είναι μια μονάδα μέτρησης του ρυθμού καρέ που χρησιμοποιείται στα γραφικά υπολογιστών, στο βίντεο και στις κινητές διεπαφές. Κάθε καρέ είναι μια στατική εικόνα που εμφανίζεται στην οθόνη για σύντομο χρονικό διάστημα. Με τη γρήγορη εναλλαγή των καρέ, ο εγκέφαλος τα αντιλαμβάνεται ως συνεχή κίνηση — αυτό το φαινόμενο ονομάζεται επιμονή της όρασης. Για εφαρμογές κινητού, το FPS είναι μια κρίσιμη μετρική, επειδή κάθε χαμένο καρέ (drop) μετατρέπει μια ομαλή κινούμενη εικόνα σε αισθητό τραυλισμό. Η εφαρμογή πρέπει να προλαβαίνει να αποδίδει κάθε καρέ αυστηρά εντός του χρονικού προϋπολογισμού: 16.6 ms για 60 FPS, 11.1 ms για 90 FPS, 8.3 ms για 120 FPS.
Το FPS μετριέται όχι μόνο για το UI, αλλά και για παιχνίδια, βίντεο και κάμερα. Στα παιχνίδια, το FPS εξαρτάται από την πολυπλοκότητα της σκηνής, την ποιότητα των υφών και την ισχύ της GPU. Στο βίντεο, το FPS είναι σταθερό (24, 30, 60 καρέ/δευτ.) και καθορίζεται από το περιεχόμενο. Σε εφαρμογές κινητού, το FPS εξαρτάται από την αποδοτικότητα του κώδικα UI: την πολυπλοκότητα του Layout, τον αριθμό των View, τη συχνότητα επανασχεδίασης και τη λειτουργία του GC (Garbage Collection). Σύμφωνα με το Apple WWDC 2022, το μέσο FPS σε μια εφαρμογή μπορεί να μειωθεί κατά 10–15% λόγω αναποτελεσματικής ενημέρωσης συλλογών (reloadData αντί για insert/delete/dequeueReusableCell). Η μέτρηση FPS σε πραγματικό χρόνο είναι συνήθης πρακτική για μηχανικούς QA και προγραμματιστές που εργάζονται στην απόδοση.
Ο υπολογισμός του FPS σε μια εφαρμογή κινητού βασίζεται στη μέτρηση του χρόνου μεταξύ διαδοχικών καρέ. Ο απλούστερος τύπος: FPS = 1000 / deltaTimeMs, όπου deltaTimeMs είναι το διάστημα μεταξύ της ολοκλήρωσης του προηγούμενου καρέ και της ολοκλήρωσης του τρέχοντος. Εάν το τρέχον καρέ αποδόθηκε σε 20 ms, FPS = 1000 / 20 = 50. Ωστόσο στην πράξη, το FPS σπάνια είναι σταθερό ακόμη και μέσα σε ένα δευτερόλεπτο: ένα τυπικό προφίλ περιλαμβάνει καρέ 12–16 ms που εναλλάσσονται με χαμένα (jank) ή αργά καρέ (40–60 ms). Ως εκ τούτου, το FPS μετριέται ως κινητός μέσος όρος για 1–5 δευτερόλεπτα ή ως εκατοστημόρια της κατανομής χρόνου καρέ.
Στο Android, το FPS υπολογίζεται μέσω του Choreographer, ο οποίος λαμβάνει callback από το VSync (παλμός συγχρονισμού οθόνης). Κάθε callback αντιστοιχεί σε ένα καρέ. Αν δεν έρθει callback — το καρέ χάθηκε. Ο Choreographer επιτρέπει την ακριβή μέτρηση του αριθμού καρέ ανά δευτερόλεπτο και του αριθμού των χαμένων (skipped frames). Στο iOS, το CADisplayLink λειτουργεί ανάλογα — καλείται κάθε φορά που η οθόνη είναι έτοιμη να αποδώσει ένα νέο καρέ. Η ιδιότητα timestamp περιέχει τον ακριβή χρόνο του τελευταίου καρέ, και το targetTimestamp — τον αναμενόμενο χρόνο του επόμενου. Η διαφορά μεταξύ τους είναι ο χρονικός προϋπολογισμός για το τρέχον καρέ.
Ο κώδικας σε Swift παρουσιάζει απλή παρακολούθηση FPS μέσω CADisplayLink. Ο μετρητής frameCount αυξάνεται σε κάθε κλήση και μία φορά το δευτερόλεπτο υπολογίζεται το πραγματικό FPS.
class FpsCounter {
private var displayLink: CADisplayLink?
private var frameCount = 0
private var lastTime = TimeInterval(0)
func start() {
displayLink = CADisplayLink(
target: self,
selector: #selector(countFrame)
)
displayLink?.add(to: .current,
forMode: .common)
}
@objc
private func countFrame() {
frameCount += 1
let now = Date().timeIntervalSince1970
if now - lastTime >= 1.0 {
print("FPS: \(frameCount)")
frameCount = 0
lastTime = now
}
}
}
Το πρότυπο 60 FPS (60 Hz) έχει εδραιωθεί στη βιομηχανία για διάφορους λόγους. Πρώτος — φυσιολογικός: το ανθρώπινο μάτι δεν διακρίνει μεμονωμένα καρέ σε συχνότητα άνω των 50–60 Hz, αντιλαμβανόμενο τα ως ομαλή κίνηση. Αυτό το όριο ονομάζεται Critical Flicker Fusion (CFF). Δεύτερος — ιστορικός: οι πρώτοι καθοδικοί σωλήνες (CRT) λειτουργούσαν στα 60 Hz στις ΗΠΑ (NTSC) και 50 Hz στην Ευρώπη (PAL). Οι σύγχρονες οθόνες LCD κληρονόμησαν αυτή τη συχνότητα. Τρίτος — μηχανικός: για κινούμενες εικόνες UI, τα 60 FPS παρέχουν υπο-χιλιοστού δευτερολέπτου καθυστέρηση απόκρισης στην αφή, η οποία είναι κρίσιμη για εισαγωγή κειμένου, κύλιση και μεταφορά.
Για προγραμματιστές κινητών, τα 60 FPS δεν είναι απλώς σύσταση, αλλά αυστηρός προϋπολογισμός 16.6 ms ανά καρέ. Αυτός ο προϋπολογισμός κατανέμεται μεταξύ όλων των φάσεων απόδοσης: Input (1–2 ms), Animation (2–3 ms), Layout (3–5 ms), Draw (3–5 ms) και Swap (1–2 ms). Εάν κάποια φάση υπερβεί τον υπο-προϋπολογισμό της, το καρέ μπορεί να μην χωρέσει στα 16.6 ms. Το Google Android Performance συνιστά να αφιερώνετε 12–14 ms για την προετοιμασία του καρέ, αφήνοντας 2–4 ms εφεδρεία για διακοπές συστήματος (GC, νήματα παρασκηνίου). Σύμφωνα με το Firebase Performance, εφαρμογές με μέσο FPS κάτω από 52 και P99 FPS κάτω από 30 λαμβάνουν 35% περισσότερες παράπονα απόδοσης στις κριτικές του Google Play.
Το FPS και το Frame Time (χρόνος καρέ) — είναι δύο όψεις του ίδιου μετρικού και είναι σημαντικό να μην τα συγχέουμε. Το FPS είναι ταχύτητα, το Frame Time είναι καθυστέρηση. Στα 60 FPS, κάθε καρέ διαρκεί 16.6 ms. Στα 30 FPS — 33.3 ms. Αλλά το FPS είναι ένα μη γραμμικό μετρικό: η πτώση από 60 σε 30 FPS σημαίνει ότι ο χρόνος καρέ διπλασιάστηκε, και η πτώση από 30 σε 20 — 1.5 φορές. Ως εκ τούτου, οι προφιλοποιητές δείχνουν όχι FPS, αλλά Frame Time — αυτό επιτρέπει να βλέπουμε τα προβληματικά καρέ, όχι τη μέση συχνότητα. Για παράδειγμα, ο μέσος όρος 55 FPS μπορεί να κρύβει ότι το 5% των καρέ έχουν Frame Time 50–100 ms — αυτά τα καρέ προκαλούν Jank, αλλά δεν επηρεάζουν πολύ το μέσο FPS.
Κατά την ανάλυση απόδοσης, συνιστάται να μην κοιτάτε το μέσο FPS, αλλά το ιστόγραμμα Frame Time. Στο Android Studio Profiler και στο iOS Instruments, το Frame Time εμφανίζεται ως κλίμακα, όπου η πράσινη ζώνη — έως 16.6 ms (60 FPS), κίτρινη — 16.6–33.3 ms (30–60 FPS), κόκκινη — πάνω από 33.3 ms (κάτω από 30 FPS). Κάθε κόκκινη στήλη είναι μια αισθητή καθυστέρηση για τον χρήστη. Πρακτικός κανόνας: το P95 Frame Time (το 95% των καρέ χωράει σε X ms) — είναι πιο αξιόπιστο μετρικό από το μέσο FPS. Εάν το P95 Frame Time υπερβαίνει τα 32 ms (30 FPS), η εφαρμογή γίνεται αντιληπτή ως αργή ακόμη και με μέσο FPS = 50.
Συνάρτηση σε Kotlin για μετατροπή πίνακα χρόνων καρέ σε FPS με εκατοστημόρια. Επιστρέφει όχι μόνο το μέσο FPS, αλλά και το P50, P90 και P99 για λεπτομερή ανάλυση.
data class FpsReport(
val average: Float,
val p50: Float,
val p90: Float,
val p99: Float
)
fun List<Long>.toFpsReport(): FpsReport {
val fpsValues = this.map { ms ->
if (ms > 0) 1000f / ms else 0f
}.sorted()
return FpsReport(
average = fpsValues.average().toFloat(),
p50 = fpsValues[fpsValues.size / 2],
p90 = fpsValues[(fpsValues.size * 90 / 100)],
p99 = fpsValues[(fpsValues.size * 99 / 100)]
)
}
Οι σύγχρονες κινητές συσκευές με οθόνες 90, 120 και 144 Hz θέτουν νέες απαιτήσεις για το FPS. Εάν μια εφαρμογή αποδίδει 60 FPS σε οθόνη 120 Hz, ο χρήστης βλέπει μικρο-τραυλισμό, επειδή κάθε δεύτερος κύκλος ανανέωσης οθόνης λαμβάνει το ίδιο καρέ. Για τη διατήρηση των 120 FPS, ο προϋπολογισμός ανά καρέ μειώνεται από 16.6 σε 8.3 ms — αυτό απαιτεί διπλάσια αποδοτικό κώδικα απόδοσης. Σύμφωνα με προγραμματιστές Android (Google I/O 2023), για την επίτευξη σταθερών 120 FPS είναι απαραίτητο: να αποφεύγονται οι δεσμεύσεις μνήμης στον βρόχο Draw, να ελαχιστοποιείται ο αριθμός των View στην ιεραρχία (λιγότερα από 80), να εγκαταλείπονται τα βαριά drawable υπέρ του VectorDrawable και να χρησιμοποιείται surfaceView για σύνθετα γραφικά.
Στο iOS, η κατάσταση είναι ανάλογη: το iPhone Pro με ProMotion (120 Hz) απαιτεί διπλάσιο αριθμό καρέ, αλλά ο χρόνος ανά καρέ είναι μισός. Η Apple σημειώνει ότι δεν χρειάζεται όλες οι κινούμενες εικόνες να λειτουργούν στα 120 FPS — το Core Animation μειώνει αυτόματα τη συχνότητα για στατικά ή αργά μεταβαλλόμενα στοιχεία. Ωστόσο, η κύλιση, οι κινήσεις χειρονομιών και οι μεταβάσεις πρέπει να αποδίδουν 120 FPS για την αίσθηση "μεταξωτότητας". Τα κύρια προβλήματα κατά τη μετάβαση από 60 σε 120 FPS: αύξηση κατανάλωσης ενέργειας (25–40% για GPU), υπερθέρμανση συσκευής και throttling — όταν η συχνότητα πέφτει λόγω υπερθέρμανσης. Συνιστάται η υλοποίηση ενός μηχανισμού fallback: εάν το Frame Time υπερβαίνει σταθερά τα 8.3 ms, μειώστε προγραμματιστικά τη συχνότητα-στόχο στα 60 FPS, αντί να περιμένετε το throttling του συστήματος.
Κώδικας σε Java για Android που καθορίζει εάν η συσκευή μπορεί να υποστηρίξει 120 FPS και αλλάζει τη λειτουργία απόδοσης. Χρησιμοποιεί Display.getMode για τον προσδιορισμό των υποστηριζόμενων συχνοτήτων.
class FpsModeSwitcher {
static boolean canDo120Fps(Activity activity) {
Display display = activity.getWindowManager()
.getDefaultDisplay();
for (Display.Mode mode : display.getSupportedModes()) {
if (mode.getRefreshRate() >= 120f) {
return true;
}
}
return false;
}
}
Η βελτιστοποίηση του FPS απαιτεί συστηματική προσέγγιση, ξεκινώντας από την προφιλοποίηση και τελειώνοντας με τον ανασχηματισμό προβληματικών περιοχών. Πρώτο στάδιο — μέτρηση του τρέχοντος FPS με προφιλοποιητή (. Δεύτερο στάδιο — εύρεση καρέ που υπερβαίνουν τον προϋπολογισμό. Για Android, αυτό μπορεί να γίνει μέσω GPU Profiling ή Perfetto. Για iOS — Instruments με το πρότυπο Core Animation. Τρίτο στάδιο — εξάλειψη των αιτιών: μείωση του overdraw, μείωση του βάθους ένθεσης View, αντικατάσταση της φάσης Layout με ConstraintLayout, προσθήκη ViewHolder Recycling, μεταφορά βαρέων υπολογισμών σε νήμα παρασκηνίου.
Οι συγκεκριμένες βελτιστοποιήσεις FPS περιλαμβάνουν: Frame Pacing — ένας μηχανισμός που κατανέμει ομοιόμορφα τον χρόνο μεταξύ των καρέ για να αποφευχθούν "πακέτα" γρήγορων και αργών καρέ. Στο Android, το Choreographer.FrameCallback με σταθερό διάστημα επιτρέπει την υλοποίηση του Frame Pacing. Στο iOS, το CADisplayLink.preferredFrameRateRange κάνει το ίδιο. Δεύτερος μηχανισμός — Triple Buffering: το σύστημα χρησιμοποιεί τρεις ενδιάμεσους bufers αντί για δύο, επιτρέποντας στην GPU να ξεκινήσει τη σχεδίαση του επόμενου καρέ χωρίς να περιμένει την απελευθέρωση του προηγούμενου. Το Android ενεργοποιεί αυτόματα το Triple Buffering όταν χρειαστεί, αλλά στο iOS, ο προγραμματιστής μπορεί να το ζητήσει ρητά μέσω του CAMetalLayer. Τρίτος — Texture Caching: προσωρινή αποθήκευση bitmap στη μνήμη GPU για να μην επαναφορτώνονται σε κάθε καρέ.
Το παράδειγμα σε Kotlin παρουσιάζει την υλοποίηση του Frame Pacing με σταθερό διάστημα 16.6 ms. Όλα τα callback έρχονται με ομοιόμορφο διάστημα, ακόμη και αν το σύστημα καθυστερεί.
class PacedFrameRenderer {
private val targetDelta = 16_666_666L // 16.6 ms (60 FPS)
private var lastFrameTime = 0L
private val frameCallback =
Choreographer.FrameCallback { frameTimeNanos ->
val delta = frameTimeNanos - lastFrameTime
if (delta >= targetDelta) {
onFrame(delta)
lastFrameTime = frameTimeNanos
}
Choreographer.getInstance()
.postFrameCallback(this)
}
private fun onFrame(delta: Long) {
// απόδοση καρέ
}
}
Συχνές ερωτήσεις
60 FPS — άνετο επίπεδο για εφαρμογές κινητού. Η διαφορά μεταξύ 60 και 120 FPS είναι αισθητή μόνο σε οθόνες με υψηλή συχνότητα ανανέωσης σε γρήγορες κινούμενες εικόνες (κύλιση, μεταφορά). Κάτω από 30 FPS — δυσφορία.
FPS = 1000 / FrameTime (ms). Εάν Frame Time = 16.6 ms, FPS = 60. Εάν Frame Time = 33.3 ms, FPS = 30. Συνιστάται η παρακολούθηση του Frame Time, όχι του FPS, καθώς δείχνει τα προβληματικά καρέ.
Κατά την κύλιση, το σύστημα καλεί Layout και Draw για κάθε νέο στοιχείο της λίστας. Εάν τα View είναι πολύπλοκα, το Layout δεν αποθηκεύεται στην κρυφή μνήμη ή χρησιμοποιούνται βαριά drawable — το Frame Time αυξάνεται και το FPS πέφτει. Λύση — ViewHolder recycling και επίπεδη ιεραρχία.
Χρησιμοποιήστε το Instruments με το πρότυπο Core Animation (δείχνει FPS σε πραγματικό χρόνο). Για προγραμματιστική μέτρηση — CADisplayLink με μέτρηση καρέ ανά δευτερόλεπτο. Για παραγωγή — MetricKit με τη μετρική MXAnimatoryMetric.
Το Triple Buffering χρησιμοποιεί τρεις bufers αντί για δύο, επιτρέποντας στην GPU να ξεκινήσει την απόδοση του επόμενου καρέ πριν ολοκληρωθεί το VSync του τρέχοντος. Αυτό εξομαλύνει τις αιχμές φόρτου και αυξάνει τη σταθερότητα του FPS, αλλά προσθέτει 1 καρέ καθυστέρησης.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης