Skip to content
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

Improve serialization performance by suppressing visibility checks in Roslyn #1729

Closed
sergeybykov opened this issue May 4, 2016 · 3 comments
Labels
area-performance All performance related issues enhancement
Milestone

Comments

@sergeybykov
Copy link
Contributor

I just learned that there is a way to avoid most if not all of run time reflection code in the codegen'ed serializers by accessing private fields directly. The trick is to suppress member visibility checks at compile and run time (via different mechanism). Roslyn supports that for scenarios like debugging.

As I understand, instead of generating the following code

private static readonly global::System.Reflection.FieldInfo field0 = typeof (global::Orleans.GrainState<T>).@GetField("State", (System.@Reflection.@BindingFlags.@Instance | System.@Reflection.@BindingFlags.@NonPublic | System.@Reflection.@BindingFlags.@Public));
private static readonly global::System.Func<global::Orleans.GrainState<T>, T> getField0 = (global::System.Func<global::Orleans.GrainState<T>, T>)global::Orleans.Serialization.SerializationManager.@GetGetter(field0);
private static readonly global::System.Action<global::Orleans.GrainState<T>, T> setField0 = (global::System.Action<global::Orleans.GrainState<T>, T>)global::Orleans.Serialization.SerializationManager.@GetReferenceSetter(field0);
...
setField0(result, (T)global::Orleans.Serialization.SerializationManager.@DeepCopyInner(getField0(input)));

we could simply generate

result.State = input.State;

The 'magic' that is required at compile time is to set two internal properties of CSharpCompilationOptions.

        var options = new CSharpCompilationOptions(targetName == "exe" ? OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary).WithAllowUnsafe(true);

        // options.MetadataImportOptions = MetadataImportOptions.All
        {
            var methodInfo = typeof(CompilationOptions).GetTypeInfo().GetDeclaredMethod("set_MetadataImportOptions");
            var type = methodInfo.GetParameters()[0].ParameterType;
            var all = Enum.ToObject(type, 2 /* MetadataImportOptions.All */);
            methodInfo.Invoke(options, new Object[] { all });
        }

        // options.TopLevelBinderFlags = true
        {
            var methodInfo = typeof(CSharpCompilationOptions).GetTypeInfo().GetDeclaredMethod("set_TopLevelBinderFlags");
            var type = methodInfo.GetParameters()[0].ParameterType;
            var ignoreAccessibility = Enum.ToObject(type, 1 << 22 /* BinderFlags.IgnoreAccessibility */);
            methodInfo.Invoke(options, new Object[] { ignoreAccessibility });
        }

At run time a special internal attribute is required to suppress visibility checks. We'd need to generate it for every assembly.

namespace System.Runtime.CompilerServices
{
    [AttributeUsageAttribute(AttributeTargets.Assembly, AllowMultiple = true)]
    internal class IgnoresAccessChecksToAttribute : Attribute
    {
        private string _assemblyName;
        public IgnoresAccessChecksToAttribute(string assemblyName)
        {
            _assemblyName = assemblyName;
        }
        public string AssemblyName 
        { 
            get { return _assemblyName; } 
        } 
    }
}

An immediate issue I see with this is that today we add generated code to the target grain interfaces/classes assemblies. This will suppress visibility checks for app code, which is obviously undesirable. I'll open a separate issue to discuss the fact that we don't really have to include generated code back to app projects. Interestingly, this approach should work right away for run time codegen.

@sergeybykov
Copy link
Contributor Author

The easiest path to explore this I think is to microbenchmark it first. Then we could try it for run time codegen, which wouldn't require any change to the compile time path.

@ReubenBond
Copy link
Member

This simplifies our code and makes it easier to support .NET Native and other AOT systems where reflection and runtime IL generation is restricted. It would be nice if we could do it on a per-class basis.

I'm all for it.

@ReubenBond
Copy link
Member

Closing for now. We can open a similar issue if we ever get support from the compiler or runtime
xref dotnet/roslyn#11149

@ghost ghost locked as resolved and limited conversation to collaborators Mar 20, 2022
@ReubenBond ReubenBond added area-performance All performance related issues and removed performance labels Mar 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-performance All performance related issues enhancement
Projects
None yet
Development

No branches or pull requests

4 participants