Skip to content

Remove all explicit GC Poll calls in unmanaged code. #113715

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Mar 24, 2025

Conversation

AaronRobinsonMSFT
Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT commented Mar 19, 2025

Remove explicit GC Poll from GC.SuppressFinalize().
Remove GC Poll from FCall and move it to managed, Buffer.__BulkMoveWithWriteBarrier().
Remove unneccessary explicit FC_GC_POLL() calls.
Remove unneccessary explicit FC_GC_POLL_RET() calls.
Remove unmanaged GCPoll implementation.
Implement JIT intrinsic for GC Poll, Thread.FastGCPoll.

Fixes #113807

@AaronRobinsonMSFT
Copy link
Member Author

@EgorBot -windows_x64 -arm64

using System;
using System.Runtime.CompilerServices;

using BenchmarkDotNet.Attributes;

public unsafe class Bench
{
    private F _f;
    private byte[] _source;
    private byte[] _dest;
    private delegate* <ref byte, ref byte, nuint, void> _method;

    class F
    {
        ~F() { }
    }

    public Bench()
    {
        _f = new F();
        _source = new byte[10 * 1024];
        _dest = new byte[_source.Length];
        Random random = new Random();
        random.NextBytes(array);

        var method = typeof(Buffer).GetMethod("BulkMoveWithWriteBarrier", BindingFlags.NonPublic | BindingFlags.Static);
        _method = (delegate* <ref byte, ref byte, nuint, void>)method.MethodHandle.GetFunctionPointer();
    }

    [Benchmark]
    public void BenchmarkBulkMoveWithWriteBarrier()
    {
        _method(ref _dest[0], ref _source[0], (nuint)dest.Length);
    }

    [Benchmark]
    public void BenchmarkSuppressFinalize()
    {
        GC.SuppressFinalize(_f);
    }
}

@AaronRobinsonMSFT
Copy link
Member Author

@EgorBot -intel -arm

using System;
using System.Reflection;
using System.Runtime.CompilerServices;

using BenchmarkDotNet.Attributes;

public unsafe class Bench
{
    private F _f;
    private byte[] _source;
    private byte[] _dest;
    private delegate* <ref byte, ref byte, nuint, void> _method;

    class F
    {
        ~F() { }
    }

    public Bench()
    {
        _f = new F();
        _source = new byte[10 * 1024];
        _dest = new byte[_source.Length];
        Random random = new Random();
        random.NextBytes(_source);

        var method = typeof(Buffer).GetMethod("BulkMoveWithWriteBarrier", BindingFlags.NonPublic | BindingFlags.Static)!;
        _method = (delegate* <ref byte, ref byte, nuint, void>)method.MethodHandle.GetFunctionPointer();
    }

    [Benchmark]
    public void BenchmarkBulkMoveWithWriteBarrier()
    {
        _method(ref _dest[0], ref _source[0], (nuint)_dest.Length);
    }

    [Benchmark]
    public void BenchmarkSuppressFinalize()
    {
        GC.SuppressFinalize(_f);
    }
}

@jkotas
Copy link
Member

jkotas commented Mar 20, 2025

@EgorBot -intel -arm

using System;
using System.Reflection;
using System.Runtime.CompilerServices;

using BenchmarkDotNet.Attributes;

public unsafe class Bench
{
    private object[] _source;
    private object[] _dest;

    public Bench()
    {
        _source = new object[10];
        _dest = new object[_source.Length];
    }

    [Benchmark]
    public void BenchmarkBulkMoveWithWriteBarrier()
    {
        _source.AsSpan().CopyTo(_dest.AsSpan());
    }
}

@jkotas
Copy link
Member

jkotas commented Mar 20, 2025

The kind of benchmark to run for BulkMoveWithWriteBarrier ^

@AaronRobinsonMSFT AaronRobinsonMSFT changed the title Remove GC Poll calls in FCalls. Remove all explicit GC Poll calls in unmanaged code. Mar 20, 2025
@AaronRobinsonMSFT
Copy link
Member Author

/cc @dotnet/jit-contrib

@AaronRobinsonMSFT AaronRobinsonMSFT marked this pull request as ready for review March 21, 2025 02:50
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request removes unnecessary explicit GC Poll calls in unmanaged code and moves this logic to managed code while also converting certain FCall methods to managed implementations.

  • In GC.CoreCLR.cs, the SuppressFinalize method is modified to only call _SuppressFinalize if the object has a finalizer.
  • In Thread.CoreCLR.cs, the unmanaged ThreadNative_PollGC call is replaced by a managed PollGCInternal wrapped by an inline PollGCWorker function.
  • In Buffer.CoreCLR.cs, an additional using directive for System.Threading is added.

Reviewed Changes

Copilot reviewed 5 out of 22 changed files in this pull request and generated 1 comment.

File Description
src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs Updates SuppressFinalize with a finalizer check to avoid unnecessary calls.
src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs Renames and wraps the GC poll call to move its implementation to managed code.
src/coreclr/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs Adds a using directive for System.Threading to support related changes.
Files not reviewed (17)
  • src/coreclr/classlibnative/bcltype/objectnative.cpp: Language not supported
  • src/coreclr/jit/compiler.h: Language not supported
  • src/coreclr/jit/compiler.hpp: Language not supported
  • src/coreclr/jit/flowgraph.cpp: Language not supported
  • src/coreclr/jit/gentree.cpp: Language not supported
  • src/coreclr/jit/gtlist.h: Language not supported
  • src/coreclr/jit/ifconversion.cpp: Language not supported
  • src/coreclr/jit/importercalls.cpp: Language not supported
  • src/coreclr/jit/liveness.cpp: Language not supported
  • src/coreclr/jit/namedintrinsiclist.h: Language not supported
  • src/coreclr/jit/optcse.cpp: Language not supported
  • src/coreclr/jit/rationalize.cpp: Language not supported
  • src/coreclr/jit/valuenum.cpp: Language not supported
  • src/coreclr/vm/comutilnative.cpp: Language not supported
  • src/coreclr/vm/comutilnative.h: Language not supported
  • src/coreclr/vm/fcall.cpp: Language not supported
  • src/coreclr/vm/fcall.h: Language not supported
Comments suppressed due to low confidence (1)

src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs:509

  • [nitpick] The local function 'PollGCWorker' could be renamed to better reflect that it is a simple wrapper for PollGCInternal; consider a more descriptive name if it improves clarity.
static void PollGCWorker() => PollGCInternal();

@AaronRobinsonMSFT
Copy link
Member Author

AaronRobinsonMSFT commented Mar 24, 2025

@jkotas I've confirmed locally that the use of the new CatchAtSafePoint() FCall does make suspension more stable. With the previous implementation I was able to observe the instability you observed as well. I'm considering the approach here as addressing the issue and fixing #113807.

@jkotas
Copy link
Member

jkotas commented Mar 24, 2025

The fix LGTM assuming that the CI passes.

@AaronRobinsonMSFT
Copy link
Member Author

For some reason, with this patch, it didn't repro in 10+ attempts:

@am11 Thanks. I've applied it and that does indeed "fix" the issue.

@AaronRobinsonMSFT AaronRobinsonMSFT merged commit 2846fb4 into dotnet:main Mar 24, 2025
164 checks passed
@AaronRobinsonMSFT AaronRobinsonMSFT deleted the hmf_remove_gcpolls branch March 24, 2025 14:58
@am11
Copy link
Member

am11 commented Mar 24, 2025

I've applied it and that does indeed "fix" the issue.

Reported upstream for a proper fix DotNetAnalyzers/StyleCopAnalyzers#3922 :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PollGC rewrite and TS_CatchAtSafePoint
6 participants