Which GUID Generation Method Wins? Performance Test of UUID, Snowflake, ThreadLocal & Atomic

An extensive performance benchmark compares four GUID generation techniques—UUID, Snowflake algorithm, ThreadLocal exclusive counters, and atomic counters—across varying thread counts, revealing that ThreadLocal excels in high‑concurrency single‑machine scenarios while Snowflake remains stable for distributed use, with detailed optimization recommendations.

FunTester
FunTester
FunTester
Which GUID Generation Method Wins? Performance Test of UUID, Snowflake, ThreadLocal & Atomic

In the Supermarket Eight tracking system, generating a globally unique identifier (GUID) is critical for log tracing and database indexing efficiency. This section quantifies the performance of four schemes—UUID, Snowflake algorithm, ThreadLocal exclusive counters, and atomic counters—through throughput tests under different concurrency levels, excluding centralized services due to network latency.

11.4.2.1 Tool Class Design

To simplify test cases and improve code readability, a UniqueUtils utility class is designed to encapsulate the four unique‑ID generation methods. The class code is shown below:

package org.funtester.performance.books.chapter11.section4;

import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Global unique identifier utility class, providing UUID, Snowflake algorithm,
 * thread‑exclusive and atomic operations.
 */
public class UniqueUtils {
    private static final SnowflakeWorker SNOW_WORKER; // Snowflake object
    private static final long ORIGIN = 1000000000000000000L; // start value
    private static final ThreadLocal<Long> EXCLUSIVE = ThreadLocal.withInitial(() -> 0L); // lock‑free counter
    private static final AtomicLong ATOMIC = new AtomicLong(0); // atomic counter

    static {
        SNOW_WORKER = new SnowflakeWorker(1, 1); // data center ID=1, machine ID=1
    }

    /** Get Snowflake unique identifier */
    public static String getSnowMark() {
        return String.valueOf(SNOW_WORKER.nextId());
    }

    /** Get UUID unique identifier */
    public static String getUUID() {
        return UUID.randomUUID().toString();
    }

    /** Get thread‑exclusive unique identifier */
    public static String getExclusiveMark() {
        Long id = EXCLUSIVE.get();
        EXCLUSIVE.set(id + 1);
        return String.valueOf(id + ORIGIN);
    }

    /** Get atomic unique identifier */
    public static String getAtomicMark() {
        return String.valueOf(ATOMIC.getAndIncrement() + ORIGIN);
    }
}

Snowflake Algorithm Optimization

To address possible clock‑rollback issues in high‑concurrency scenarios, the SnowflakeWorker.nextId() method is enhanced with fault‑tolerant logic:

public synchronized long nextId() {
    long timestamp = System.currentTimeMillis();
    if (timestamp < lastTimestamp) {
        if (lastTimestamp - timestamp >= 2000L) {
            throw new RuntimeException("Clock moved backwards more than 2 seconds, refusing to generate ID, time difference " + (lastTimestamp - timestamp) + " ms");
        } else {
            timestamp = lastTimestamp; // use last timestamp
        }
    }
    if (lastTimestamp == timestamp) {
        sequence = (sequence + 1) & SEQUENCE_MASK;
        if (sequence == 0) {
            timestamp = nextMillis(lastTimestamp);
        }
    } else {
        sequence = 0L;
    }
    lastTimestamp = timestamp;
    long id = ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT) |
               (dataId << DATA_CENTER_ID_SHIFT) |
               (workerId << WORKER_ID_SHIFT) |
               sequence;
    return id & Long.MAX_VALUE;
}

If the rollback is less than 2 seconds, the previous timestamp is reused to avoid interruption; otherwise an exception is thrown to signal a clock problem.

11.4.2.2 Test Results and Analysis

Throughput (ops/μs) was measured for 1, 10, 40, and 100 threads. Summary tables:

Method

Score (ops/μs)

atomic

78.779

exclusive

72.593

uuid

11.706

snow

4.096

Method

Score (ops/μs)

exclusive

248.632

atomic

18.964

uuid

4.639

snow

4.095

Method

Score (ops/μs)

exclusive

353.736

atomic

19.621

uuid

3.989

snow

4.096

Method

Score (ops/μs)

exclusive

374.932

atomic

19.993

uuid

4.661

snow

4.096

ThreadLocal (exclusive) : Best performance in high‑concurrency (10, 40, 100 threads), scaling from 72.593 ops/μs (1 thread) to 374.932 ops/μs (100 threads) due to lock‑free ThreadLocal operations.

Atomic : Highest single‑thread throughput (78.779 ops/μs) but degrades under contention, falling to ~20 ops/μs at 100 threads.

UUID : Lowest throughput (≈4–5 ops/μs) because of string generation and random computation overhead; suitable for non‑performance‑critical logging.

Snowflake : Stable but lowest performance (≈4 ops/μs) limited by synchronized locking and bit‑wise calculations; fits distributed scenarios but not single‑machine high‑concurrency.

Concurrency Trend : Lock‑free solutions (exclusive) dominate as threads increase, while atomic suffers CAS contention and UUID/Snowflake remain comparatively low.

Supermarket Eight Application

Single‑machine high concurrency : Prefer ThreadLocal (exclusive) for request IDs, balancing throughput and thread isolation.

Distributed scenario : Use the optimized Snowflake to generate order IDs across multiple nodes.

Log tracing : Employ UUID for readable IDs that aid debugging.

Atomic fallback : AtomicInteger is a secondary choice for simple incremental IDs in moderate concurrency.

11.4.3.3 Optimization Recommendations

For single‑machine tracking, prioritize ThreadLocal combined with FunObjPool reuse of UserBehavior objects to reduce allocation.

Deploy the optimized Snowflake with proper data‑center and machine IDs and monitor clock synchronization.

Validate ThreadLocal stability at higher thread counts (e.g., 200 threads).

Adjust ORIGIN in UniqueUtils to shorten ID length for exclusive and atomic schemes, saving storage.

After code or JDK upgrades, re‑run performance tests to ensure no new bottlenecks.

11.4.3 Summary

This section compared the performance of UUID, Snowflake, ThreadLocal, and atomic unique‑ID generators, highlighting the superiority of ThreadLocal in single‑machine high‑concurrency environments and the suitability of Snowflake for distributed use. The Supermarket Eight team should adopt ThreadLocal for tracking IDs and integrate Snowflake for order IDs, while continuously monitoring and optimizing the implementations to support millions of users with stable, efficient identifier generation.

JavaSnowflakeThreadLocalGUID
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.