[PR #15774] [MERGED] Optimize GetUniqueFlags<T>() #14415

Closed
opened 2026-02-07 07:29:26 +03:00 by OVERLORD · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/jellyfin/jellyfin/pull/15774
Author: @stevenaw
Created: 12/12/2025
Status: Merged
Merged: 12/12/2025
Merged by: @Bond-009

Base: masterHead: optimize-GetUniqueFlags


📝 Commits (1)

  • ef73ed6 optimize GetUniqueFlags()

📊 Changes

1 file changed (+3 additions, -3 deletions)

View changed files

📝 src/Jellyfin.Extensions/EnumerableExtensions.cs (+3 -3)

📄 Description

Fixes https://github.com/jellyfin/jellyfin/issues/15773

Changes
Updates this helper to use the generic Enum.GetValues() method which has been internally optimized to use the generic type information efficiently. This and the knock on elimination of two casts make this 8x faster and allocate 1/7 the memory.

Benchmarks

BenchmarkDotNet v0.15.6, Windows 11 (10.0.26100.7462/24H2/2024Update/HudsonValley)
13th Gen Intel Core i7-1355U 1.70GHz, 1 CPU, 12 logical and 10 physical cores
.NET SDK 10.0.101
  [Host]     : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3
  DefaultJob : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3


| Method   | Mean      | Error    | StdDev   | Code Size | Gen0   | Allocated |
|--------- |----------:|---------:|---------:|----------:|-------:|----------:|
| Original | 414.80 ns | 6.381 ns | 5.969 ns |   3,954 B | 0.2470 |    1552 B |
| New      |  53.66 ns | 0.667 ns | 0.557 ns |   3,602 B | 0.0356 |     224 B |
Benchmark Code
[SimpleJob, MemoryDiagnoser, DisassemblyDiagnoser]
public class EnumFlagsBenchmark
{
    [Benchmark]
    public TranscodeReason[] Original()
    {
        return GetUniqueFlags(TranscodeReason.RefFramesNotSupported | TranscodeReason.AudioBitrateNotSupported | TranscodeReason.ContainerNotSupported).ToArray();

        static IEnumerable<T> GetUniqueFlags<T>( T flags)
            where T : struct, Enum
        {
            foreach (Enum value in Enum.GetValues(typeof(T)))
            {
                if (flags.HasFlag(value))
                {
                    yield return (T)value;
                }
            }
        }
    }

    [Benchmark]
    public TranscodeReason[] New()
    {
        return GetUniqueFlags(TranscodeReason.RefFramesNotSupported | TranscodeReason.AudioBitrateNotSupported | TranscodeReason.ContainerNotSupported).ToArray();

        static IEnumerable<T> GetUniqueFlags<T>(T flags)
            where T : struct, Enum
        {
            foreach (T value in Enum.GetValues<T>())
            {
                if (flags.HasFlag(value))
                {
                    yield return value;
                }
            }
        }
    }

    [Flags]
    public enum TranscodeReason
    {
        // Primary
        ContainerNotSupported = 1 << 0,
        VideoCodecNotSupported = 1 << 1,
        AudioCodecNotSupported = 1 << 2,
        SubtitleCodecNotSupported = 1 << 3,
        AudioIsExternal = 1 << 4,
        SecondaryAudioNotSupported = 1 << 5,
        StreamCountExceedsLimit = 1 << 26,

        // Video Constraints
        VideoProfileNotSupported = 1 << 6,
        VideoRangeTypeNotSupported = 1 << 24,
        VideoCodecTagNotSupported = 1 << 25,
        VideoLevelNotSupported = 1 << 7,
        VideoResolutionNotSupported = 1 << 8,
        VideoBitDepthNotSupported = 1 << 9,
        VideoFramerateNotSupported = 1 << 10,
        RefFramesNotSupported = 1 << 11,
        AnamorphicVideoNotSupported = 1 << 12,
        InterlacedVideoNotSupported = 1 << 13,

        // Audio Constraints
        AudioChannelsNotSupported = 1 << 14,
        AudioProfileNotSupported = 1 << 15,
        AudioSampleRateNotSupported = 1 << 16,
        AudioBitDepthNotSupported = 1 << 17,

        // Bitrate Constraints
        ContainerBitrateExceedsLimit = 1 << 18,
        VideoBitrateNotSupported = 1 << 19,
        AudioBitrateNotSupported = 1 << 20,

        // Errors
        UnknownVideoStreamInfo = 1 << 21,
        UnknownAudioStreamInfo = 1 << 22,
        DirectPlayError = 1 << 23,
    }
}

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/jellyfin/jellyfin/pull/15774 **Author:** [@stevenaw](https://github.com/stevenaw) **Created:** 12/12/2025 **Status:** ✅ Merged **Merged:** 12/12/2025 **Merged by:** [@Bond-009](https://github.com/Bond-009) **Base:** `master` ← **Head:** `optimize-GetUniqueFlags` --- ### 📝 Commits (1) - [`ef73ed6`](https://github.com/jellyfin/jellyfin/commit/ef73ed6ef7fd6bf2925d1ff503adaa4091d4a766) optimize GetUniqueFlags() ### 📊 Changes **1 file changed** (+3 additions, -3 deletions) <details> <summary>View changed files</summary> 📝 `src/Jellyfin.Extensions/EnumerableExtensions.cs` (+3 -3) </details> ### 📄 Description Fixes https://github.com/jellyfin/jellyfin/issues/15773 **Changes** Updates this helper to use the generic [Enum.GetValues<T>()](https://github.com/dotnet/dotnet/blob/b0f34d51fccc69fd334253924abd8d6853fad7aa/src/runtime/src/libraries/System.Private.CoreLib/src/System/Enum.cs#L306) method which has been internally optimized to use the generic type information efficiently. This and the knock on elimination of two casts make this 8x faster and allocate 1/7 the memory. **Benchmarks** ``` BenchmarkDotNet v0.15.6, Windows 11 (10.0.26100.7462/24H2/2024Update/HudsonValley) 13th Gen Intel Core i7-1355U 1.70GHz, 1 CPU, 12 logical and 10 physical cores .NET SDK 10.0.101 [Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 DefaultJob : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 | Method | Mean | Error | StdDev | Code Size | Gen0 | Allocated | |--------- |----------:|---------:|---------:|----------:|-------:|----------:| | Original | 414.80 ns | 6.381 ns | 5.969 ns | 3,954 B | 0.2470 | 1552 B | | New | 53.66 ns | 0.667 ns | 0.557 ns | 3,602 B | 0.0356 | 224 B | ``` <details> <summary>Benchmark Code</summary> ```csharp [SimpleJob, MemoryDiagnoser, DisassemblyDiagnoser] public class EnumFlagsBenchmark { [Benchmark] public TranscodeReason[] Original() { return GetUniqueFlags(TranscodeReason.RefFramesNotSupported | TranscodeReason.AudioBitrateNotSupported | TranscodeReason.ContainerNotSupported).ToArray(); static IEnumerable<T> GetUniqueFlags<T>( T flags) where T : struct, Enum { foreach (Enum value in Enum.GetValues(typeof(T))) { if (flags.HasFlag(value)) { yield return (T)value; } } } } [Benchmark] public TranscodeReason[] New() { return GetUniqueFlags(TranscodeReason.RefFramesNotSupported | TranscodeReason.AudioBitrateNotSupported | TranscodeReason.ContainerNotSupported).ToArray(); static IEnumerable<T> GetUniqueFlags<T>(T flags) where T : struct, Enum { foreach (T value in Enum.GetValues<T>()) { if (flags.HasFlag(value)) { yield return value; } } } } [Flags] public enum TranscodeReason { // Primary ContainerNotSupported = 1 << 0, VideoCodecNotSupported = 1 << 1, AudioCodecNotSupported = 1 << 2, SubtitleCodecNotSupported = 1 << 3, AudioIsExternal = 1 << 4, SecondaryAudioNotSupported = 1 << 5, StreamCountExceedsLimit = 1 << 26, // Video Constraints VideoProfileNotSupported = 1 << 6, VideoRangeTypeNotSupported = 1 << 24, VideoCodecTagNotSupported = 1 << 25, VideoLevelNotSupported = 1 << 7, VideoResolutionNotSupported = 1 << 8, VideoBitDepthNotSupported = 1 << 9, VideoFramerateNotSupported = 1 << 10, RefFramesNotSupported = 1 << 11, AnamorphicVideoNotSupported = 1 << 12, InterlacedVideoNotSupported = 1 << 13, // Audio Constraints AudioChannelsNotSupported = 1 << 14, AudioProfileNotSupported = 1 << 15, AudioSampleRateNotSupported = 1 << 16, AudioBitDepthNotSupported = 1 << 17, // Bitrate Constraints ContainerBitrateExceedsLimit = 1 << 18, VideoBitrateNotSupported = 1 << 19, AudioBitrateNotSupported = 1 << 20, // Errors UnknownVideoStreamInfo = 1 << 21, UnknownAudioStreamInfo = 1 << 22, DirectPlayError = 1 << 23, } } ``` </details> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
OVERLORD added the pull-request label 2026-02-07 07:29:26 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/jellyfin#14415