How to Generate UUID in Delphi: Complete Guide with Examples

Understanding UUIDs in Delphi Development

Universally Unique Identifiers (UUIDs), also known as GUIDs (Globally Unique Identifiers) in Windows development, are 128-bit values that provide unique identification across distributed systems. In Delphi programming, GUIDs are essential for COM interfaces, database primary keys, distributed applications, and ensuring uniqueness without centralized coordination. Whether you're developing desktop applications with VCL/FMX or server applications, understanding UUID generation is crucial.

This comprehensive guide explores multiple approaches to generate UUIDs in Delphi applications, covering both native RTL functions and Windows API calls. We'll examine implementation strategies for different Delphi versions, from classic Delphi 7 to the latest RAD Studio releases, as well as cross-platform considerations for FireMonkey applications. The techniques presented here also apply to Free Pascal and Lazarus development.

4 Effective Methods to Generate UUIDs in Delphi

Method 1: Using CreateGUID Function

The simplest and most common way to generate a UUID in Delphi is using the built-in CreateGUID function from the System.SysUtils unit. This method works across all Delphi versions and platforms.

program GenerateUUID;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

var
  MyGUID: TGUID;
  GUIDString: string;
begin
  try
    // Generate a new GUID
    if CreateGUID(MyGUID) = S_OK then
    begin
      // Convert GUID to string representation
      GUIDString := GUIDToString(MyGUID);
      
      WriteLn('Generated UUID: ', GUIDString);
      // Output example: Generated UUID: {8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75}
      
      // Alternative: Get string without braces
      GUIDString := MyGUID.ToString;
      WriteLn('Without braces: ', GUIDString);
      // Output example: Without braces: 8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75
    end
    else
      WriteLn('Failed to create GUID');
      
    ReadLn;
  except
    on E: Exception do
      WriteLn('Error: ', E.Message);
  end;
end.

CreateGUID generates a Version 4 (random) UUID using the operating system's cryptographically secure random number generator. On Windows, it calls CoCreateGuid internally, while on other platforms it uses appropriate system functions. The function returns S_OK (0) on success. The TGUID record type represents the GUID structure, and GUIDToString converts it to the standard string format with braces.

Method 2: Direct Windows API Call

For Windows-specific applications or when you need more control, you can directly call the Windows API CoCreateGuid function. This approach requires the Winapi.ActiveX unit.

program WindowsAPIGUID;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  Winapi.Windows,
  Winapi.ActiveX;

function GenerateWindowsGUID: string;
var
  NewGUID: TGUID;
  hr: HResult;
begin
  // Initialize COM (required for CoCreateGuid)
  CoInitialize(nil);
  try
    // Call Windows API directly
    hr := CoCreateGuid(NewGUID);
    
    if Succeeded(hr) then
    begin
      // Convert to string format
      Result := GUIDToString(NewGUID);
    end
    else
    begin
      raise Exception.CreateFmt('CoCreateGuid failed with error: %d', [hr]);
    end;
  finally
    // Cleanup COM
    CoUninitialize;
  end;
end;

begin
  try
    WriteLn('Windows API GUID: ', GenerateWindowsGUID);
    
    // Generate multiple GUIDs
    WriteLn('Generating 5 GUIDs:');
    for var i := 1 to 5 do
      WriteLn(Format('  GUID %d: %s', [i, GenerateWindowsGUID]));
      
    ReadLn;
  except
    on E: Exception do
      WriteLn('Error: ', E.Message);
  end;
end.

This method directly interfaces with Windows COM subsystem to generate GUIDs. CoCreateGuid is the underlying Windows API that CreateGUID calls on Windows platforms. While this approach requires COM initialization, it provides direct access to Windows GUID generation and can be useful when working with COM interfaces or when you need to ensure you're using the Windows-specific implementation.

Method 3: Custom Formatted GUIDs

Delphi allows flexible formatting of GUIDs to meet various requirements, such as removing braces, using different separators, or creating custom representations for specific systems.

program FormattedGUIDs;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

type
  TGUIDHelper = record helper for TGUID
    function ToCompactString: string;
    function ToUppercaseString: string;
    function ToRegistryFormat: string;
    function ToURNFormat: string;
  end;

function TGUIDHelper.ToCompactString: string;
begin
  // Remove all formatting (32 hex digits)
  Result := Format('%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x',
    [D1, D2, D3, D4[0], D4[1], D4[2], D4[3], D4[4], D4[5], D4[6], D4[7]]);
end;

function TGUIDHelper.ToUppercaseString: string;
begin
  Result := UpperCase(ToString);
end;

function TGUIDHelper.ToRegistryFormat: string;
begin
  // Registry format uses braces
  Result := GUIDToString(Self);
end;

function TGUIDHelper.ToURNFormat: string;
begin
  // URN format: urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  Result := 'urn:uuid:' + ToString;
end;

var
  MyGUID: TGUID;
begin
  CreateGUID(MyGUID);
  
  WriteLn('Standard format:    ', MyGUID.ToString);
  WriteLn('With braces:        ', GUIDToString(MyGUID));
  WriteLn('Compact (no dashes):', MyGUID.ToCompactString);
  WriteLn('Uppercase:          ', MyGUID.ToUppercaseString);
  WriteLn('Registry format:    ', MyGUID.ToRegistryFormat);
  WriteLn('URN format:         ', MyGUID.ToURNFormat);
  
  // Custom formatting with specific separators
  WriteLn('Custom format:      ',
    Format('%.8x_%.4x_%.4x_%.2x%.2x_%.2x%.2x%.2x%.2x%.2x%.2x',
    [MyGUID.D1, MyGUID.D2, MyGUID.D3, MyGUID.D4[0], MyGUID.D4[1],
     MyGUID.D4[2], MyGUID.D4[3], MyGUID.D4[4], MyGUID.D4[5],
     MyGUID.D4[6], MyGUID.D4[7]]));
  
  ReadLn;
end.

This example demonstrates various GUID formatting options using a record helper. The TGUID record contains fields D1 (longword), D2 and D3 (word), and D4 (array of 8 bytes) that represent the GUID structure. You can access these fields directly for custom formatting. The compact format is useful for URLs or when storage space is limited, while the URN format is used in various Internet standards.

Method 4: Deterministic UUID Generation

While random GUIDs are suitable for most cases, sometimes you need deterministic UUIDs that can be regenerated from specific inputs. This implementation creates Version 5 UUIDs using SHA-1 hashing.

program DeterministicGUID;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Hash;

type
  // Standard namespace GUIDs from RFC 4122
  TNamespaceGUID = record
    const DNS: TGUID = '{6ba7b810-9dad-11d1-80b4-00c04fd430c8}';
    const URL: TGUID = '{6ba7b811-9dad-11d1-80b4-00c04fd430c8}';
    const OID: TGUID = '{6ba7b812-9dad-11d1-80b4-00c04fd430c8}';
    const X500: TGUID = '{6ba7b814-9dad-11d1-80b4-00c04fd430c8}';
  end;

function CreateNameBasedGUID(const NamespaceGUID: TGUID; const Name: string): TGUID;
var
  Hash: THashSHA1;
  HashBytes: TBytes;
  NamespaceBytes: TBytes;
  NameBytes: TBytes;
  CombinedBytes: TBytes;
  i: Integer;
begin
  // Convert namespace GUID to bytes
  SetLength(NamespaceBytes, 16);
  Move(NamespaceGUID.D1, NamespaceBytes[0], 4);
  Move(NamespaceGUID.D2, NamespaceBytes[4], 2);
  Move(NamespaceGUID.D3, NamespaceBytes[6], 2);
  Move(NamespaceGUID.D4[0], NamespaceBytes[8], 8);
  
  // Swap byte order for network byte order (big-endian)
  // D1 is 4 bytes
  for i := 0 to 1 do
  begin
    var temp := NamespaceBytes[i];
    NamespaceBytes[i] := NamespaceBytes[3-i];
    NamespaceBytes[3-i] := temp;
  end;
  // D2 is 2 bytes
  var temp := NamespaceBytes[4];
  NamespaceBytes[4] := NamespaceBytes[5];
  NamespaceBytes[5] := temp;
  // D3 is 2 bytes  
  temp := NamespaceBytes[6];
  NamespaceBytes[6] := NamespaceBytes[7];
  NamespaceBytes[7] := temp;
  
  // Convert name to UTF-8 bytes
  NameBytes := TEncoding.UTF8.GetBytes(Name);
  
  // Combine namespace and name
  SetLength(CombinedBytes, Length(NamespaceBytes) + Length(NameBytes));
  Move(NamespaceBytes[0], CombinedBytes[0], Length(NamespaceBytes));
  Move(NameBytes[0], CombinedBytes[Length(NamespaceBytes)], Length(NameBytes));
  
  // Calculate SHA-1 hash
  Hash := THashSHA1.Create;
  Hash.Update(CombinedBytes);
  HashBytes := Hash.HashAsBytes;
  
  // Copy first 16 bytes to GUID
  Move(HashBytes[0], Result.D1, 4);
  Move(HashBytes[4], Result.D2, 2);
  Move(HashBytes[6], Result.D3, 2);
  Move(HashBytes[8], Result.D4[0], 8);
  
  // Set version (5) and variant bits
  Result.D3 := (Result.D3 and $0FFF) or $5000; // Version 5
  Result.D4[0] := (Result.D4[0] and $3F) or $80; // Variant 10
end;

begin
  try
    var TestName := 'example.com/user/12345';
    
    // Generate deterministic GUIDs
    var GUID1 := CreateNameBasedGUID(TNamespaceGUID.URL, TestName);
    WriteLn('URL-based GUID for "', TestName, '":');
    WriteLn('  ', GUIDToString(GUID1));
    
    // Same input produces same output
    var GUID2 := CreateNameBasedGUID(TNamespaceGUID.URL, TestName);
    WriteLn('  Same input: ', GUIDToString(GUID2));
    WriteLn('  Equal: ', GUIDToString(GUID1) = GUIDToString(GUID2));
    
    WriteLn;
    
    // Different namespaces produce different GUIDs
    var GUID3 := CreateNameBasedGUID(TNamespaceGUID.DNS, 'example.com');
    WriteLn('DNS-based GUID for "example.com":');
    WriteLn('  ', GUIDToString(GUID3));
    
    ReadLn;
  except
    on E: Exception do
      WriteLn('Error: ', E.Message);
  end;
end.

This implementation creates RFC 4122 Version 5 UUIDs using SHA-1 hashing. The same namespace and name combination always produces the same UUID, making it ideal for distributed systems where different nodes need to generate identical identifiers for the same resource. The namespace provides context (DNS, URL, etc.) while the name is the specific identifier. Note that byte order conversion is necessary because the RFC specifies network byte order.

Creating a Reusable UUID Utility Unit

For applications that frequently use UUIDs, it's beneficial to create a utility unit that centralizes UUID generation logic. Here's a comprehensive utility unit for Delphi applications:

unit UUIDUtils;

interface

uses
  System.SysUtils, System.Hash;

type
  /// <summary>
  /// Utility class for UUID/GUID generation and manipulation
  /// </summary>
  TUUIDGenerator = class
  public
    type
      TUUIDVersion = (uvRandom, uvTimeBased, uvNameBased);
      
    class var
      // Standard namespaces from RFC 4122
      NamespaceDNS: TGUID;
      NamespaceURL: TGUID;
      NamespaceOID: TGUID;
      NamespaceX500: TGUID;
      
  private
    class constructor Create;
    class function SwapEndian(const AGUID: TGUID): TGUID;
    
  public
    /// <summary>Generate a random UUID (Version 4)</summary>
    class function NewGUID: TGUID;
    
    /// <summary>Generate a sequential UUID for database optimization</summary>
    class function NewSequentialGUID: TGUID;
    
    /// <summary>Generate a name-based UUID (Version 5)</summary>
    class function NewNameBasedGUID(const NamespaceGUID: TGUID; 
      const Name: string): TGUID;
      
    /// <summary>Generate URL-based deterministic UUID</summary>
    class function NewURLBasedGUID(const URL: string): TGUID;
    
    /// <summary>Generate DNS-based deterministic UUID</summary>
    class function NewDNSBasedGUID(const DNS: string): TGUID;
    
    /// <summary>Parse string to GUID with validation</summary>
    class function TryParseGUID(const S: string; out GUID: TGUID): Boolean;
    
    /// <summary>Check if GUID is empty/nil</summary>
    class function IsEmptyGUID(const GUID: TGUID): Boolean;
    
    /// <summary>Get GUID without braces or hyphens</summary>
    class function GetCompactGUID(const GUID: TGUID): string;
  end;

implementation

uses
  System.DateUtils, Winapi.Windows;

{ TUUIDGenerator }

class constructor TUUIDGenerator.Create;
begin
  // Initialize standard namespaces
  NamespaceDNS := StringToGUID('{6ba7b810-9dad-11d1-80b4-00c04fd430c8}');
  NamespaceURL := StringToGUID('{6ba7b811-9dad-11d1-80b4-00c04fd430c8}');
  NamespaceOID := StringToGUID('{6ba7b812-9dad-11d1-80b4-00c04fd430c8}');
  NamespaceX500 := StringToGUID('{6ba7b814-9dad-11d1-80b4-00c04fd430c8}');
end;

class function TUUIDGenerator.NewGUID: TGUID;
begin
  if CreateGUID(Result) <> S_OK then
    raise Exception.Create('Failed to create GUID');
end;

class function TUUIDGenerator.NewSequentialGUID: TGUID;
var
  Timestamp: Int64;
  Counter: Word;
begin
  // Generate base GUID
  Result := NewGUID;
  
  // Get current timestamp
  Timestamp := DateTimeToUnix(Now, False) * 10000000;
  
  // Embed timestamp in first 6 bytes for sequential ordering
  Result.D1 := LongWord(Timestamp shr 16);
  Result.D2 := Word(Timestamp);
  
  // Keep version 4 and variant bits
  Result.D3 := (Result.D3 and $0FFF) or $4000;
  Result.D4[0] := (Result.D4[0] and $3F) or $80;
end;

class function TUUIDGenerator.SwapEndian(const AGUID: TGUID): TGUID;
begin
  Result := AGUID;
  // Swap D1 (4 bytes)
  Result.D1 := ((AGUID.D1 and $000000FF) shl 24) or
               ((AGUID.D1 and $0000FF00) shl 8) or
               ((AGUID.D1 and $00FF0000) shr 8) or
               ((AGUID.D1 and $FF000000) shr 24);
  // Swap D2 (2 bytes)
  Result.D2 := ((AGUID.D2 and $00FF) shl 8) or
               ((AGUID.D2 and $FF00) shr 8);
  // Swap D3 (2 bytes)
  Result.D3 := ((AGUID.D3 and $00FF) shl 8) or
               ((AGUID.D3 and $FF00) shr 8);
end;

class function TUUIDGenerator.NewNameBasedGUID(const NamespaceGUID: TGUID;
  const Name: string): TGUID;
var
  Hash: THashSHA1;
  HashBytes: TBytes;
  NamespaceBytes: TBytes;
  SwappedGUID: TGUID;
begin
  // Convert to network byte order
  SwappedGUID := SwapEndian(NamespaceGUID);
  
  // Prepare data for hashing
  SetLength(NamespaceBytes, 16);
  Move(SwappedGUID, NamespaceBytes[0], 16);
  
  // Calculate hash
  Hash := THashSHA1.Create;
  Hash.Update(NamespaceBytes);
  Hash.Update(TEncoding.UTF8.GetBytes(Name));
  HashBytes := Hash.HashAsBytes;
  
  // Create GUID from hash
  Move(HashBytes[0], Result, 16);
  
  // Convert back from network byte order
  Result := SwapEndian(Result);
  
  // Set version 5 and variant
  Result.D3 := (Result.D3 and $0FFF) or $5000;
  Result.D4[0] := (Result.D4[0] and $3F) or $80;
end;

class function TUUIDGenerator.NewURLBasedGUID(const URL: string): TGUID;
begin
  Result := NewNameBasedGUID(NamespaceURL, URL);
end;

class function TUUIDGenerator.NewDNSBasedGUID(const DNS: string): TGUID;
begin
  Result := NewNameBasedGUID(NamespaceDNS, DNS);
end;

class function TUUIDGenerator.TryParseGUID(const S: string; 
  out GUID: TGUID): Boolean;
begin
  try
    GUID := StringToGUID(S);
    Result := True;
  except
    Result := False;
  end;
end;

class function TUUIDGenerator.IsEmptyGUID(const GUID: TGUID): Boolean;
const
  EmptyGUID: TGUID = '{00000000-0000-0000-0000-000000000000}';
begin
  Result := CompareMem(@GUID, @EmptyGUID, SizeOf(TGUID));
end;

class function TUUIDGenerator.GetCompactGUID(const GUID: TGUID): string;
begin
  Result := Format('%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x',
    [GUID.D1, GUID.D2, GUID.D3, GUID.D4[0], GUID.D4[1], GUID.D4[2], 
     GUID.D4[3], GUID.D4[4], GUID.D4[5], GUID.D4[6], GUID.D4[7]]);
end;

end.

This utility unit provides a comprehensive set of UUID generation methods encapsulated in a class with class methods. It includes random UUID generation, sequential UUIDs for database optimization, and deterministic name-based UUIDs following RFC 4122. The unit handles endianness conversion for proper Version 5 UUID generation and provides helper methods for parsing and formatting. You can easily extend this unit with additional functionality like Version 1 (time-based) or Version 3 (MD5-based) UUID generation.

Parsing and Validating GUIDs in Delphi

When working with GUIDs from external sources or user input, proper validation is crucial. Here's how to parse and validate GUIDs in Delphi applications:

program ValidateGUIDs;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, System.RegularExpressions;

type
  TGUIDValidator = class
  private
    const
      // RFC 4122 compliant GUID pattern
      GUIDPattern = '^[{(]?[0-9A-Fa-f]{8}[-]?([0-9A-Fa-f]{4}[-]?){3}[0-9A-Fa-f]{12}[)}]?$';
      
  public
    class function IsValidGUID(const Value: string): Boolean;
    class function TryParseGUID(const Value: string; out GUID: TGUID): Boolean;
    class function NormalizeGUID(const Value: string): string;
    class function ExtractGUIDFromText(const Text: string): TArray<string>;
  end;

class function TGUIDValidator.IsValidGUID(const Value: string): Boolean;
var
  RegEx: TRegEx;
begin
  RegEx := TRegEx.Create(GUIDPattern);
  Result := RegEx.IsMatch(Value);
end;

class function TGUIDValidator.TryParseGUID(const Value: string; 
  out GUID: TGUID): Boolean;
var
  NormalizedValue: string;
begin
  Result := False;
  
  // First check with regex
  if not IsValidGUID(Value) then
    Exit;
    
  try
    // Normalize the GUID string
    NormalizedValue := NormalizeGUID(Value);
    
    // Try to convert
    GUID := StringToGUID(NormalizedValue);
    Result := True;
  except
    // Invalid format
    Result := False;
  end;
end;

class function TGUIDValidator.NormalizeGUID(const Value: string): string;
var
  CleanValue: string;
begin
  // Remove all non-hex characters
  CleanValue := TRegEx.Replace(Value, '[^0-9A-Fa-f]', '');
  
  // Check if we have exactly 32 hex digits
  if Length(CleanValue) <> 32 then
    raise Exception.Create('Invalid GUID length');
    
  // Format as standard GUID
  Result := Format('{%s-%s-%s-%s-%s}',
    [Copy(CleanValue, 1, 8),
     Copy(CleanValue, 9, 4),
     Copy(CleanValue, 13, 4),
     Copy(CleanValue, 17, 4),
     Copy(CleanValue, 21, 12)]);
end;

class function TGUIDValidator.ExtractGUIDFromText(const Text: string): TArray<string>;
var
  RegEx: TRegEx;
  Matches: TMatchCollection;
  Match: TMatch;
  i: Integer;
begin
  RegEx := TRegEx.Create(GUIDPattern);
  Matches := RegEx.Matches(Text);
  
  SetLength(Result, Matches.Count);
  for i := 0 to Matches.Count - 1 do
    Result[i] := Matches[i].Value;
end;

// Test the validator
var
  TestStrings: array[0..5] of string = (
    '{8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75}',  // Valid with braces
    '8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75',    // Valid without braces
    '8C2D56F08A1E4B6CA8D2AF3A99B11F75',        // Valid without any formatting
    '(8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75)',  // Valid with parentheses
    'not-a-guid',                               // Invalid
    '8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F7'      // Invalid (too short)
  );
  TestGUID: TGUID;
  TestString: string;
begin
  try
    WriteLn('GUID Validation Tests:');
    WriteLn('='*50);
    
    for TestString in TestStrings do
    begin
      Write(Format('%-40s: ', [TestString]));
      
      if TGUIDValidator.TryParseGUID(TestString, TestGUID) then
        WriteLn('Valid - Parsed as ', GUIDToString(TestGUID))
      else
        WriteLn('Invalid');
    end;
    
    WriteLn;
    WriteLn('Extracting GUIDs from text:');
    WriteLn('='*50);
    
    var SampleText := 'The user ID is {8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75} ' +
      'and the session ID is 123e4567-e89b-12d3-a456-426614174000.';
    
    var ExtractedGUIDs := TGUIDValidator.ExtractGUIDFromText(SampleText);
    
    WriteLn('Text: ', SampleText);
    WriteLn('Found GUIDs:');
    for TestString in ExtractedGUIDs do
      WriteLn('  - ', TestString);
      
    ReadLn;
  except
    on E: Exception do
      WriteLn('Error: ', E.Message);
  end;
end.

This validation example demonstrates comprehensive GUID validation using regular expressions. The TGUIDValidator class can validate GUIDs in various formats (with/without braces, hyphens, or parentheses), normalize them to a standard format, and extract GUIDs from larger text blocks. The regex pattern matches all common GUID representations while the normalization function ensures consistent formatting. This is particularly useful when processing user input or data from external systems.

Performance Optimization for GUID Generation

When generating GUIDs at scale in Delphi applications, performance becomes critical. Here's how to optimize GUID generation and measure performance:

program GUIDPerformance;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, System.Diagnostics, System.Classes, 
  System.Generics.Collections, System.Threading;

type
  TGUIDCache = class
  private
    FCache: TThreadList<TGUID>;
    FCacheSize: Integer;
    FRefillThreshold: Integer;
    
    procedure RefillCache;
  public
    constructor Create(ACacheSize: Integer = 1000);
    destructor Destroy; override;
    
    function GetGUID: TGUID;
    property CacheSize: Integer read FCacheSize;
  end;

constructor TGUIDCache.Create(ACacheSize: Integer);
begin
  inherited Create;
  FCacheSize := ACacheSize;
  FRefillThreshold := ACacheSize div 4;
  FCache := TThreadList<TGUID>.Create;
  RefillCache;
end;

destructor TGUIDCache.Destroy;
begin
  FCache.Free;
  inherited;
end;

procedure TGUIDCache.RefillCache;
var
  List: TList<TGUID>;
  i: Integer;
  NewGUID: TGUID;
begin
  List := FCache.LockList;
  try
    // Fill cache to capacity
    while List.Count < FCacheSize do
    begin
      CreateGUID(NewGUID);
      List.Add(NewGUID);
    end;
  finally
    FCache.UnlockList;
  end;
end;

function TGUIDCache.GetGUID: TGUID;
var
  List: TList<TGUID>;
begin
  List := FCache.LockList;
  try
    if List.Count = 0 then
      RefillCache;
      
    Result := List.Last;
    List.Delete(List.Count - 1);
    
    // Refill cache asynchronously if below threshold
    if List.Count < FRefillThreshold then
    begin
      TTask.Run(procedure
      begin
        RefillCache;
      end);
    end;
  finally
    FCache.UnlockList;
  end;
end;

// Benchmark different approaches
procedure BenchmarkGUIDGeneration(Count: Integer);
var
  Stopwatch: TStopwatch;
  GUIDs: TArray<TGUID>;
  i: Integer;
  Cache: TGUIDCache;
  
  procedure BenchmarkStandard;
  begin
    Stopwatch := TStopwatch.StartNew;
    SetLength(GUIDs, Count);
    
    for i := 0 to Count - 1 do
      CreateGUID(GUIDs[i]);
      
    Stopwatch.Stop;
    WriteLn(Format('Standard CreateGUID: %d ms for %d GUIDs (%.2f GUIDs/ms)',
      [Stopwatch.ElapsedMilliseconds, Count, 
       Count / Max(1, Stopwatch.ElapsedMilliseconds)]));
  end;
  
  procedure BenchmarkCached;
  begin
    Cache := TGUIDCache.Create(1000);
    try
      Stopwatch := TStopwatch.StartNew;
      SetLength(GUIDs, Count);
      
      for i := 0 to Count - 1 do
        GUIDs[i] := Cache.GetGUID;
        
      Stopwatch.Stop;
      WriteLn(Format('Cached generation: %d ms for %d GUIDs (%.2f GUIDs/ms)',
        [Stopwatch.ElapsedMilliseconds, Count,
         Count / Max(1, Stopwatch.ElapsedMilliseconds)]));
    finally
      Cache.Free;
    end;
  end;
  
  procedure BenchmarkParallel;
  begin
    Stopwatch := TStopwatch.StartNew;
    SetLength(GUIDs, Count);
    
    TParallel.For(0, Count - 1, 
      procedure(Index: Integer)
      begin
        CreateGUID(GUIDs[Index]);
      end);
      
    Stopwatch.Stop;
    WriteLn(Format('Parallel generation: %d ms for %d GUIDs (%.2f GUIDs/ms)',
      [Stopwatch.ElapsedMilliseconds, Count,
       Count / Max(1, Stopwatch.ElapsedMilliseconds)]));
  end;
  
begin
  WriteLn(Format('Benchmarking generation of %d GUIDs...', [Count]));
  WriteLn('='*60);
  
  BenchmarkStandard;
  BenchmarkCached;
  BenchmarkParallel;
  
  WriteLn;
  WriteLn('Memory usage:');
  WriteLn(Format('  Single GUID: %d bytes', [SizeOf(TGUID)]));
  WriteLn(Format('  %d GUIDs: %.2f MB', [Count, (Count * SizeOf(TGUID)) / (1024 * 1024)]));
end;

begin
  try
    // Test with different sizes
    BenchmarkGUIDGeneration(10000);
    WriteLn;
    BenchmarkGUIDGeneration(100000);
    WriteLn;
    BenchmarkGUIDGeneration(1000000);
    
    ReadLn;
  except
    on E: Exception do
      WriteLn('Error: ', E.Message);
  end;
end.

This performance optimization example demonstrates three approaches to high-volume GUID generation: standard sequential generation, cached generation with background refilling, and parallel generation using TParallel. The cached approach pre-generates GUIDs and refills the cache asynchronously, reducing latency for applications that need GUIDs on-demand. Parallel generation leverages multiple CPU cores for bulk operations. The benchmarks help identify the most suitable approach for your specific use case. Note that CreateGUID is thread-safe, making parallel generation safe without additional synchronization.

Conclusion: Best Practices for UUID Generation in Delphi

When implementing UUID generation in Delphi applications, choose the appropriate method based on your requirements. For general-purpose unique identifiers, CreateGUID provides a simple, cross-platform solution. For Windows-specific applications requiring COM compatibility, direct API calls offer more control. Sequential GUIDs optimize database performance but sacrifice some randomness. Deterministic UUIDs enable distributed systems to generate consistent identifiers independently. Always validate external GUIDs and consider caching for high-performance scenarios.

Modern Delphi development benefits from the robust GUID support built into the RTL, with additional capabilities available through Windows APIs and custom implementations. Whether you're building desktop applications with VCL, cross-platform apps with FireMonkey, or server applications, the techniques presented in this guide provide a solid foundation for UUID implementation. Remember to consider thread safety, performance implications, and storage requirements when designing your UUID strategy. For database applications, evaluate whether sequential GUIDs provide meaningful performance benefits for your specific use case.

For quick UUID generation without writing code, you can use our online UUID generator tool, which provides instant UUID generation in various formats with a simple click.

© UUIDGenerator.co v1.0 All rights reserved (2025)