Token导航 LogoToken导航TokenDH.com
待分类需要联网github未标认证来源可访问许可证需确认审计通过

dotnet-performance-patternsdotnet 性能模式

Agent Skill

dotnet-performance-patterns 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

624

周安装

25

GitHub Stars

15

下载量

202
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:dotnet-performance-patterns(dotnet 性能模式)
来源仓库:https://github.com/wshaddix/dotnet-skills
仓库路径:skills/dotnet-performance-patterns
安装命令:
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-performance-patterns
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-performance-patterns

简介

提供面向性能的架构模式指导,涵盖零分配编码、缓冲池和字符串处理优化。

  • 适用于需要提升 .NET 应用性能的场景,重点讲解性能原理而非语法细节。
  • 基于 .NET 8.0+ 环境设计,推荐使用 Span<T> 和 Memory<T> 等现代特性。
  • 安装前需确认项目版本兼容性,避免在不支持的框架上应用相关模式。
  • dotnet-performance-patterns 属于待分类类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

dotnet-performance-patterns

Performance-oriented architecture patterns for.NET applications. Covers zero-allocation coding with Span<T> and Memory<T>, buffer pooling with ArrayPool<T>, struct design for performance (readonly struct, ref struct, in parameters), sealed class devirtualization by the JIT, stack-based allocation with stackalloc, and string handling performance. Focuses on the why (performance rationale and measurement) rather than the how (language syntax).

Version assumptions:.NET 8.0+ baseline. Span<T> and Memory<T> are available from.NET Core 2.1+ but this skill targets modern usage patterns on.NET 8+.

Out of scope: C# language syntax for Span, records, pattern matching, and collection expressions -- see [skill:dotnet-csharp-modern-patterns]. Coding standards and naming conventions (including sealed class style guidance) -- see [skill:dotnet-csharp-coding-standards]. Microbenchmarking setup and measurement is owned by this epic's companion skill -- see [skill:dotnet-benchmarkdotnet]. Native AOT compilation pipeline and trimming -- see [skill:dotnet-native-aot]. Serialization format performance tradeoffs -- see [skill:dotnet-serialization]. Architecture patterns (caching, resilience, DI) -- see [skill:dotnet-architecture-patterns]. EF Core query optimization -- see [skill:dotnet-efcore-patterns].

Cross-references: [skill:dotnet-benchmarkdotnet] for measuring the impact of these patterns, [skill:dotnet-csharp-modern-patterns] for Span/Memory syntax foundation, [skill:dotnet-csharp-coding-standards] for sealed class style conventions, [skill:dotnet-native-aot] for AOT performance characteristics and trimming impact on pattern choices, [skill:dotnet-serialization] for serialization performance context.


Span<T> and Memory<T> for Zero-Allocation Scenarios

Why Span<T> Matters for Performance

Span<T> provides a safe, bounds-checked view over contiguous memory without allocating. It enables slicing arrays, strings, and stack memory without copying. For syntax details see [skill:dotnet-csharp-modern-patterns]; this section focuses on performance rationale.

Zero-Allocation String Processing

// BAD: Substring allocates a new string on each call
public static (string Key, string Value) ParseHeader_Allocating(string header)
{
    var colonIndex = header.IndexOf(':');
    return (header.Substring(0, colonIndex), header.Substring(colonIndex + 1).Trim());
}

// GOOD: ReadOnlySpan<char> slicing avoids all allocations
public static (ReadOnlySpan<char> Key, ReadOnlySpan<char> Value) ParseHeader_ZeroAlloc(
    ReadOnlySpan<char> header)
{
    var colonIndex = header.IndexOf(':');
    return (header[..colonIndex], header[(colonIndex + 1)..].Trim());
}

Performance impact: for high-throughput parsing (HTTP headers, log lines, CSV rows), Span-based parsing eliminates GC pressure entirely. Measure with [MemoryDiagnoser] in [skill:dotnet-benchmarkdotnet] -- the Allocated column should read 0 B.

Memory<T> for Async and Storage Scenarios

Span<T> cannot be used in async methods or stored on the heap (it is a ref struct). Use Memory<T> when you need to:

  • Pass buffers to async I/O methods
  • Store a slice reference in a field or collection
  • Return a memory region from a method for later consumption
public async Task<int> ReadAndProcessAsync(Stream stream, Memory<byte> buffer)
{
    var bytesRead = await stream.ReadAsync(buffer);
    var data = buffer[..bytesRead]; // Memory<T> slicing -- no allocation
    return ProcessData(data.Span);  // .Span for synchronous processing
}

private int ProcessData(ReadOnlySpan<byte> data)
{
    var sum = 0;
    foreach (var b in data)
        sum += b;
    return sum;
}

ArrayPool<T> for Buffer Pooling

Why Pool Buffers

Large array allocations (>= 85,000 bytes) go directly to the Large Object Heap (LOH), which is only collected in Gen 2 GC -- expensive and causes pauses. Even smaller arrays add GC pressure in hot paths. ArrayPool<T> rents and returns buffers to avoid repeated allocations.

Usage Pattern

using System.Buffers;

public int ProcessLargeData(Stream source)
{
    var buffer = ArrayPool<byte>.Shared.Rent(minimumLength: 81920);
    try
    {
        var bytesRead = source.Read(buffer, 0, buffer.Length);
        // IMPORTANT: Rent may return a larger buffer than requested.
        // Always use bytesRead or the requested length, never buffer.Length.
        return ProcessChunk(buffer.AsSpan(0, bytesRead));
    }
    finally
    {
        ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
        // clearArray: true zeroes the buffer -- use when buffer held sensitive data
    }
}

Common Mistakes

MistakeImpactFix
Using buffer.Length instead of requested sizeProcesses uninitialized bytes beyond actual dataTrack requested/actual size separately
Forgetting to return the bufferPool exhaustion, falls back to allocationUse try/finally or a using wrapper
Returning a buffer twiceCorrupts pool stateNull out the reference after return
Not clearing sensitive dataSecurity leak from pooled buffersPass clearArray: true to Return

readonly struct, ref struct, and in Parameters

readonly struct -- Defensive Copy Elimination

The JIT must defensively copy non-readonly structs when accessed via in, readonly fields, or readonly methods to prevent mutation. Marking a struct readonly guarantees immutability, eliminating these copies:

// GOOD: readonly eliminates defensive copies on every access
public readonly struct Point3D
{
    public double X { get; }
    public double Y { get; }
    public double Z { get; }

    public Point3D(double x, double y, double z) => (X, Y, Z) = (x, y, z);

    // readonly struct: JIT knows this cannot mutate, no defensive copy needed
    public double DistanceTo(in Point3D other)
    {
        var dx = X - other.X;
        var dy = Y - other.Y;
        var dz = Z - other.Z;
        return Math.Sqrt(dx * dx + dy * dy + dz * dz);
    }
}

Without readonly, calling a method on a struct through an in parameter forces the JIT to copy the entire struct to protect against mutation. For large structs in tight loops, this eliminates significant overhead.

ref struct -- Stack-Only Types

ref struct types are constrained to the stack. They cannot be boxed, stored in fields, or used in async methods. This enables safe wrapping of Span<T>:

public ref struct SpanLineEnumerator
{
    private ReadOnlySpan<char> _remaining;

    public SpanLineEnumerator(ReadOnlySpan<char> text) => _remaining = text;

    public ReadOnlySpan<char> Current { get; private set; }

    public bool MoveNext()
    {
        if (_remaining.IsEmpty)
            return false;

        var newlineIndex = _remaining.IndexOf('\n');
        if (newlineIndex == -1)
        {
            Current = _remaining;
            _remaining = default;
        }
        else
        {
            Current = _remaining[..newlineIndex];
            _remaining = _remaining[(newlineIndex + 1)..];
        }
        return true;
    }
}

in Parameters -- Pass-by-Reference Without Mutation

Use in for large readonly structs passed to methods. The in modifier passes by reference (avoids copying) and prevents mutation:

// in parameter: pass by reference, no copy, no mutation allowed
public static double CalculateDistance(in Point3D a, in Point3D b)
    => a.DistanceTo(in b);

When to use in:

Struct SizeRecommendation
<= 16 bytesPass by value (register-friendly, no indirection overhead)
> 16 bytesUse in to avoid copy overhead
Any size, readonly structin is safe (no defensive copies)
Any size, non-readonly structAvoid in (defensive copies negate the benefit)

Sealed Class Performance Rationale

JIT Devirtualization

When a class is sealed, the JIT can replace virtual method calls with direct calls (devirtualization) because no subclass override is possible. This enables further inlining:

// Without sealed: virtual dispatch through vtable
public class OpenService : IProcessor
{
    public virtual int Process(int x) => x * 2;
}

// With sealed: JIT devirtualizes + inlines Process call
public sealed class SealedService : IProcessor
{
    public int Process(int x) => x * 2;
}

public interface IProcessor { int Process(int x); }

Verify devirtualization with [DisassemblyDiagnoser] in [skill:dotnet-benchmarkdotnet]. See [skill:dotnet-csharp-coding-standards] for the project convention of defaulting to sealed classes.

Performance Impact

Devirtualization + inlining eliminates:

  1. vtable lookup -- indirect memory access to find the method pointer
  2. Call overhead -- the actual indirect call instruction
  3. Inlining barrier -- virtual calls cannot be inlined; sealed methods can

In tight loops and hot paths, the cumulative effect is measurable. For framework/library types that are not designed for extension, always prefer sealed.


stackalloc for Small Stack-Based Allocations

When to Use stackalloc

stackalloc allocates memory on the stack, avoiding GC entirely. Use for small, fixed-size buffers in hot paths:

public static string FormatGuid(Guid guid)
{
    // 68 bytes on the stack -- well within safe limits
    Span<char> buffer = stackalloc char[68];
    guid.TryFormat(buffer, out var charsWritten, "D");
    return new string(buffer[..charsWritten]);
}

Safety Guidelines

GuidelineRationale
Keep allocations small (< 1 KB typical, < 4 KB absolute maximum)Stack space is limited (~1 MB default on Windows); overflow crashes the process
Use constant or bounded sizes onlyRuntime-variable sizes risk stack overflow from malicious/unexpected input
Prefer Span<T> assignment over raw pointerSpan provides bounds checking; raw pointers do not
Fall back to ArrayPool for large/variable sizesGracefully handle cases that exceed stack budget

Hybrid Pattern: stackalloc with ArrayPool Fallback

public static string ProcessData(ReadOnlySpan<byte> input)
{
    const int stackThreshold = 256;
    char[]? rented = null;

    Span<char> buffer = input.Length <= stackThreshold
        ? stackalloc char[stackThreshold]
        : (rented = ArrayPool<char>.Shared.Rent(input.Length));

    try
    {
        var written = Encoding.UTF8.GetChars(input, buffer);
        return new string(buffer[..written]);
    }
    finally
    {
        if (rented is not null)
            ArrayPool<char>.Shared.Return(rented);
    }
}

This pattern is used throughout the.NET runtime libraries and is the recommended approach for methods that handle both small and large inputs.


String Interning and StringComparison Performance

String Comparison Performance

Ordinal comparisons are significantly faster than culture-aware comparisons because they avoid Unicode normalization:

// FAST: ordinal comparison (byte-by-byte)
bool isMatch = str.Equals("expected", StringComparison.Ordinal);
bool containsKey = dict.ContainsKey(key); // Dictionary<string, T> uses ordinal by default

// FAST: case-insensitive ordinal (no culture overhead)
bool isMatchIgnoreCase = str.Equals("expected", StringComparison.OrdinalIgnoreCase);

// SLOW: culture-aware comparison (Unicode normalization, linguistic rules)
bool isMatchCulture = str.Equals("expected", StringComparison.CurrentCulture);

Default guidance: Use StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase for internal identifiers, dictionary keys, file paths, and protocol strings. Reserve culture-aware comparison for user-visible text sorting and display.

String Interning

The CLR interns compile-time string literals automatically. string.Intern() can reduce memory for runtime strings that repeat frequently:

// Intern frequently-repeated runtime strings to share a single instance
var normalized = string.Intern(headerName.ToLowerInvariant());

Caution: Interned strings are never garbage collected. Only intern strings from a bounded, known set (HTTP headers, XML element names). Never intern user input or unbounded data.

Efficient String Building

ScenarioRecommended ApproachWhy
2-3 concatenationsString interpolation $"{a}{b}"Compiler optimizes to string.Concat
Loop concatenationStringBuilderAvoids quadratic allocation
Known fixed partsstring.CreateSingle allocation, Span-based writing
High-throughput formattingSpan<char> + TryFormatZero-allocation formatting
// string.Create for single-allocation building
public static string FormatId(int category, int item)
{
    return string.Create(11, (category, item), static (span, state) =>
    {
        state.category.TryFormat(span, out var catWritten);
        span[catWritten] = '-';
        state.item.TryFormat(span[(catWritten + 1)..], out _);
    });
}

Performance Measurement Checklist

Before applying any optimization pattern, measure first. Premature optimization without data leads to complex code with no measurable benefit.

  1. Identify the hot path -- use [skill:dotnet-benchmarkdotnet] to establish a baseline
  2. Measure allocations -- enable [MemoryDiagnoser] and check the Allocated column
  3. Apply one pattern at a time -- change one thing, re-measure, compare to baseline
  4. Check AOT impact -- if targeting Native AOT ([skill:dotnet-native-aot]), verify patterns are trim-safe
  5. Verify with production-like data -- synthetic benchmarks can miss real-world allocation patterns
  6. Document the tradeoff -- every optimization trades readability or flexibility for speed; record the measured gain

Agent Gotchas

  1. Measure before optimizing -- never apply Span/ArrayPool/stackalloc without a benchmark showing the allocation or latency problem. Premature optimization produces unreadable code for no measurable gain.
  2. Do not use stackalloc with variable sizes from untrusted input -- stack overflow crashes the process with no exception handler. Always validate bounds or use the hybrid stackalloc/ArrayPool pattern.
  3. Always mark value types readonly struct when they are immutable -- without readonly, the JIT generates defensive copies on every in parameter access and readonly field access, silently negating the performance benefit of using structs.
  4. Return rented ArrayPool buffers in finally blocks -- forgetting to return starves the pool and causes fallback allocations that negate the benefit.
  5. Use StringComparison.Ordinal for internal comparisons -- omitting the comparison parameter defaults to culture-aware comparison, which is slower and produces surprising results for technical strings (file paths, identifiers).
  6. Sealed classes help performance only when the JIT can see the concrete type -- if the object is accessed through an interface variable in a non-devirtualizable call site, sealing provides no benefit. Verify with [DisassemblyDiagnoser].
  7. Do not re-teach language syntax -- reference [skill:dotnet-csharp-modern-patterns] for Span/Memory syntax details. This skill focuses on when and why to use these patterns for performance.

Knowledge Sources

Performance patterns in this skill are grounded in guidance from:

  • Stephen Toub --.NET Performance blog series (devblogs.microsoft.com/dotnet/author/toub). Authoritative source on Span<T>, ValueTask, ArrayPool, async internals, and runtime performance characteristics.
  • Stephen Cleary -- Async best practices and concurrent collections guidance. Author of *Concurrency in C# Cookbook*.
  • Nick Chapsas -- Modern.NET performance patterns and benchmarking methodology.
These sources inform the patterns and rationale presented above. This skill does not claim to represent or speak for any individual.

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.47%
按下载量换算68

Claude

29.8%
按下载量换算60

Cursor

18.32%
按下载量换算37

Gemini CLI

9.14%
按下载量换算18

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills