Initial prototype of Activator.CreateFactory - #45458

Closed
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance
Closed

Initial prototype of Activator.CreateFactory#45458
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance

Conversation

@GrabYourPitchforks

@GrabYourPitchforksGrabYourPitchforks commented Dec 2, 2020

Copy link
Copy Markdown
Member

** DRAFT ** DRAFT ** DRAFT **

Contributes to #36194. Doesn't fully resolve that issue since there's still a second set APIs which aren't present in this PR.

This is the follow-up to #32520, which takes advantage of that refactoring to introduce new Activator.CreateFactory(Type) and Activator.CreateFactory<T> APIs. Think of a factory as a cached version of CreateInstance, where we've already done all the initial work and can keep invoking our factory over and over every time we need a new instance.

As a reminder, the purpose of this new API is two-fold. First, it allows callers to remove their own usage of ref-emit. Second, it tries to avoid spinning up the JIT as much as possible. Activator.CreateFactory(Type) should be JIT-free in nearly all cases, including for value types. Activator.CreateFactory<T> may spin up the JIT for value types T.

I've also hooked this factory up through System.Text.Json to show how it can replace their existing usage of DynamicMethod in some scenarios.

This is a draft PR and isn't fully ready for review. There's still some code cleanup that needs to take place, but it should give a good indication of how things should look when they're fully finished.

To-do:

  • Make mono runtime compile and tests pass
  • Figure out whether the Delegate changes were appropriate or if they should be backed out
  • Fix layering in Activator.cs and Activator.CoreCLR.cs
  • Fix suppressions and dynamic dependency attributes introduced here
  • Add API docs

There are some other uses of Activator.CreateInstance (see here for one example), but I didn't change those call sites as part of this PR since I don't think they're on hot paths.

/cc @layomia and @jozkee as area owners of System.Text.Json so that they can see how this would look in practice. If you're wondering about the double-delegate pattern, see #45408 and #36194 (comment) for context.

@Dotnet-GitSync-Bot

Copy link
Copy Markdown
Collaborator

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

}

#if NET6_0 // should really be NET6_0_OR_GREATER
return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are 20 instance of ConstructorDelegate in 7 files. I think we should just replace it with Func<object>.

Assert.NotNull(factory);
StructWithPublicDefaultConstructor instance = factory();
Assert.True(instance.ConstructorInvoked);
Assert.True(IsAddressOnLocalStack(instance.AddressPassedToConstructor)); // using Func<T>, value type ctor is called using ref to stack local, no boxing

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is implementation detail. I can imagine some implementations may choose to allocate the box to keep things simple.


// Then create a delegate to factory.CreateInstance, closed over the "this" parameter.

return (Func<object?>)factory.CreateDelegate((RuntimeType)typeof(Func<object?>));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why can't this just call new Func<object?>(factory.CreateInstance) ? This looks slow and complicated for no good reason.

{
if (typeof(T).IsValueType)
{
IActivationFactory factory = (IActivationFactory)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ActivationFactory<>), (RuntimeType)typeof(T));

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CreateInstanceForAnotherGenericParameter is AOT unfriendly API that depends on JIT. Can this be just something something like this?

class StructFactory<T>
{
T CreateInstance()
{
T value;
_pfnConstructor(&value);
return value;
}
}
return new Func<T>(new StructFactory<T>().CreateInstance);

?

@ViktorHofer

Copy link
Copy Markdown
Member

// Auto-generated message

69e114c which was merged 12/7 removed the intermediate src/coreclr/src/ folder. This PR needs to be updated as it touches files in that directory which causes conflicts.

To update your commits you can use this bash script: https://gist.github.com/ViktorHofer/6d24f62abdcddb518b4966ead5ef3783. Feel free to use the comment section of the gist to improve the script for others.

@ghostghost closed this Jan 25, 2021
@ghost

Copy link
Copy Markdown

Draft Pull Request was automatically closed for inactivity. It can be manually reopened in the next 30 days if the work resumes.

@steveharter

Copy link
Copy Markdown
Contributor

@GrabYourPitchforks based on prior discussion I assume the perf numbers are the same as ref.emit (once warmed up). Is that correct?

@GrabYourPitchforks

GrabYourPitchforks commented Jan 28, 2021

Copy link
Copy Markdown
MemberAuthor

@steveharter Yes, it should meet or beat ref emit in terms of wall clock time + memory utilization.

@ghostghost locked as resolved and limited conversation to collaborators Feb 27, 2021
This pull request was closed.
Sign up for freeto 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.

5 participants

@GrabYourPitchforks@Dotnet-GitSync-Bot@ViktorHofer@steveharter@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Initial prototype of Activator.CreateFactory - #45458

Closed
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance
Closed

Initial prototype of Activator.CreateFactory#45458
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance

Conversation

@GrabYourPitchforks

@GrabYourPitchforksGrabYourPitchforks commented Dec 2, 2020

Copy link
Copy Markdown
Member

** DRAFT ** DRAFT ** DRAFT **

Contributes to #36194. Doesn't fully resolve that issue since there's still a second set APIs which aren't present in this PR.

This is the follow-up to #32520, which takes advantage of that refactoring to introduce new Activator.CreateFactory(Type) and Activator.CreateFactory<T> APIs. Think of a factory as a cached version of CreateInstance, where we've already done all the initial work and can keep invoking our factory over and over every time we need a new instance.

As a reminder, the purpose of this new API is two-fold. First, it allows callers to remove their own usage of ref-emit. Second, it tries to avoid spinning up the JIT as much as possible. Activator.CreateFactory(Type) should be JIT-free in nearly all cases, including for value types. Activator.CreateFactory<T> may spin up the JIT for value types T.

I've also hooked this factory up through System.Text.Json to show how it can replace their existing usage of DynamicMethod in some scenarios.

This is a draft PR and isn't fully ready for review. There's still some code cleanup that needs to take place, but it should give a good indication of how things should look when they're fully finished.

To-do:

  • Make mono runtime compile and tests pass
  • Figure out whether the Delegate changes were appropriate or if they should be backed out
  • Fix layering in Activator.cs and Activator.CoreCLR.cs
  • Fix suppressions and dynamic dependency attributes introduced here
  • Add API docs

There are some other uses of Activator.CreateInstance (see here for one example), but I didn't change those call sites as part of this PR since I don't think they're on hot paths.

/cc @layomia and @jozkee as area owners of System.Text.Json so that they can see how this would look in practice. If you're wondering about the double-delegate pattern, see #45408 and #36194 (comment) for context.

@Dotnet-GitSync-Bot

Copy link
Copy Markdown
Collaborator

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

}

#if NET6_0 // should really be NET6_0_OR_GREATER
return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are 20 instance of ConstructorDelegate in 7 files. I think we should just replace it with Func<object>.

Assert.NotNull(factory);
StructWithPublicDefaultConstructor instance = factory();
Assert.True(instance.ConstructorInvoked);
Assert.True(IsAddressOnLocalStack(instance.AddressPassedToConstructor)); // using Func<T>, value type ctor is called using ref to stack local, no boxing

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is implementation detail. I can imagine some implementations may choose to allocate the box to keep things simple.


// Then create a delegate to factory.CreateInstance, closed over the "this" parameter.

return (Func<object?>)factory.CreateDelegate((RuntimeType)typeof(Func<object?>));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why can't this just call new Func<object?>(factory.CreateInstance) ? This looks slow and complicated for no good reason.

{
if (typeof(T).IsValueType)
{
IActivationFactory factory = (IActivationFactory)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ActivationFactory<>), (RuntimeType)typeof(T));

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CreateInstanceForAnotherGenericParameter is AOT unfriendly API that depends on JIT. Can this be just something something like this?

class StructFactory<T>
{
T CreateInstance()
{
T value;
_pfnConstructor(&value);
return value;
}
}
return new Func<T>(new StructFactory<T>().CreateInstance);

?

@ViktorHofer

Copy link
Copy Markdown
Member

// Auto-generated message

69e114c which was merged 12/7 removed the intermediate src/coreclr/src/ folder. This PR needs to be updated as it touches files in that directory which causes conflicts.

To update your commits you can use this bash script: https://gist.github.com/ViktorHofer/6d24f62abdcddb518b4966ead5ef3783. Feel free to use the comment section of the gist to improve the script for others.

@ghostghost closed this Jan 25, 2021
@ghost

Copy link
Copy Markdown

Draft Pull Request was automatically closed for inactivity. It can be manually reopened in the next 30 days if the work resumes.

@steveharter

Copy link
Copy Markdown
Contributor

@GrabYourPitchforks based on prior discussion I assume the perf numbers are the same as ref.emit (once warmed up). Is that correct?

@GrabYourPitchforks

GrabYourPitchforks commented Jan 28, 2021

Copy link
Copy Markdown
MemberAuthor

@steveharter Yes, it should meet or beat ref emit in terms of wall clock time + memory utilization.

@ghostghost locked as resolved and limited conversation to collaborators Feb 27, 2021
This pull request was closed.
Sign up for freeto 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.

5 participants

@GrabYourPitchforks@Dotnet-GitSync-Bot@ViktorHofer@steveharter@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Initial prototype of Activator.CreateFactory - #45458

Closed
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance
Closed

Initial prototype of Activator.CreateFactory#45458
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance

Conversation

@GrabYourPitchforks

@GrabYourPitchforksGrabYourPitchforks commented Dec 2, 2020

Copy link
Copy Markdown
Member

** DRAFT ** DRAFT ** DRAFT **

Contributes to #36194. Doesn't fully resolve that issue since there's still a second set APIs which aren't present in this PR.

This is the follow-up to #32520, which takes advantage of that refactoring to introduce new Activator.CreateFactory(Type) and Activator.CreateFactory<T> APIs. Think of a factory as a cached version of CreateInstance, where we've already done all the initial work and can keep invoking our factory over and over every time we need a new instance.

As a reminder, the purpose of this new API is two-fold. First, it allows callers to remove their own usage of ref-emit. Second, it tries to avoid spinning up the JIT as much as possible. Activator.CreateFactory(Type) should be JIT-free in nearly all cases, including for value types. Activator.CreateFactory<T> may spin up the JIT for value types T.

I've also hooked this factory up through System.Text.Json to show how it can replace their existing usage of DynamicMethod in some scenarios.

This is a draft PR and isn't fully ready for review. There's still some code cleanup that needs to take place, but it should give a good indication of how things should look when they're fully finished.

To-do:

  • Make mono runtime compile and tests pass
  • Figure out whether the Delegate changes were appropriate or if they should be backed out
  • Fix layering in Activator.cs and Activator.CoreCLR.cs
  • Fix suppressions and dynamic dependency attributes introduced here
  • Add API docs

There are some other uses of Activator.CreateInstance (see here for one example), but I didn't change those call sites as part of this PR since I don't think they're on hot paths.

/cc @layomia and @jozkee as area owners of System.Text.Json so that they can see how this would look in practice. If you're wondering about the double-delegate pattern, see #45408 and #36194 (comment) for context.

@Dotnet-GitSync-Bot

Copy link
Copy Markdown
Collaborator

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

}

#if NET6_0 // should really be NET6_0_OR_GREATER
return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are 20 instance of ConstructorDelegate in 7 files. I think we should just replace it with Func<object>.

Assert.NotNull(factory);
StructWithPublicDefaultConstructor instance = factory();
Assert.True(instance.ConstructorInvoked);
Assert.True(IsAddressOnLocalStack(instance.AddressPassedToConstructor)); // using Func<T>, value type ctor is called using ref to stack local, no boxing

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is implementation detail. I can imagine some implementations may choose to allocate the box to keep things simple.


// Then create a delegate to factory.CreateInstance, closed over the "this" parameter.

return (Func<object?>)factory.CreateDelegate((RuntimeType)typeof(Func<object?>));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why can't this just call new Func<object?>(factory.CreateInstance) ? This looks slow and complicated for no good reason.

{
if (typeof(T).IsValueType)
{
IActivationFactory factory = (IActivationFactory)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ActivationFactory<>), (RuntimeType)typeof(T));

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CreateInstanceForAnotherGenericParameter is AOT unfriendly API that depends on JIT. Can this be just something something like this?

class StructFactory<T>
{
T CreateInstance()
{
T value;
_pfnConstructor(&value);
return value;
}
}
return new Func<T>(new StructFactory<T>().CreateInstance);

?

@ViktorHofer

Copy link
Copy Markdown
Member

// Auto-generated message

69e114c which was merged 12/7 removed the intermediate src/coreclr/src/ folder. This PR needs to be updated as it touches files in that directory which causes conflicts.

To update your commits you can use this bash script: https://gist.github.com/ViktorHofer/6d24f62abdcddb518b4966ead5ef3783. Feel free to use the comment section of the gist to improve the script for others.

@ghostghost closed this Jan 25, 2021
@ghost

Copy link
Copy Markdown

Draft Pull Request was automatically closed for inactivity. It can be manually reopened in the next 30 days if the work resumes.

@steveharter

Copy link
Copy Markdown
Contributor

@GrabYourPitchforks based on prior discussion I assume the perf numbers are the same as ref.emit (once warmed up). Is that correct?

@GrabYourPitchforks

GrabYourPitchforks commented Jan 28, 2021

Copy link
Copy Markdown
MemberAuthor

@steveharter Yes, it should meet or beat ref emit in terms of wall clock time + memory utilization.

@ghostghost locked as resolved and limited conversation to collaborators Feb 27, 2021
This pull request was closed.
Sign up for freeto 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.

5 participants

@GrabYourPitchforks@Dotnet-GitSync-Bot@ViktorHofer@steveharter@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Initial prototype of Activator.CreateFactory - #45458

Closed
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance
Closed

Initial prototype of Activator.CreateFactory#45458
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance

Conversation

@GrabYourPitchforks

@GrabYourPitchforksGrabYourPitchforks commented Dec 2, 2020

Copy link
Copy Markdown
Member

** DRAFT ** DRAFT ** DRAFT **

Contributes to #36194. Doesn't fully resolve that issue since there's still a second set APIs which aren't present in this PR.

This is the follow-up to #32520, which takes advantage of that refactoring to introduce new Activator.CreateFactory(Type) and Activator.CreateFactory<T> APIs. Think of a factory as a cached version of CreateInstance, where we've already done all the initial work and can keep invoking our factory over and over every time we need a new instance.

As a reminder, the purpose of this new API is two-fold. First, it allows callers to remove their own usage of ref-emit. Second, it tries to avoid spinning up the JIT as much as possible. Activator.CreateFactory(Type) should be JIT-free in nearly all cases, including for value types. Activator.CreateFactory<T> may spin up the JIT for value types T.

I've also hooked this factory up through System.Text.Json to show how it can replace their existing usage of DynamicMethod in some scenarios.

This is a draft PR and isn't fully ready for review. There's still some code cleanup that needs to take place, but it should give a good indication of how things should look when they're fully finished.

To-do:

  • Make mono runtime compile and tests pass
  • Figure out whether the Delegate changes were appropriate or if they should be backed out
  • Fix layering in Activator.cs and Activator.CoreCLR.cs
  • Fix suppressions and dynamic dependency attributes introduced here
  • Add API docs

There are some other uses of Activator.CreateInstance (see here for one example), but I didn't change those call sites as part of this PR since I don't think they're on hot paths.

/cc @layomia and @jozkee as area owners of System.Text.Json so that they can see how this would look in practice. If you're wondering about the double-delegate pattern, see #45408 and #36194 (comment) for context.

@Dotnet-GitSync-Bot

Copy link
Copy Markdown
Collaborator

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

}

#if NET6_0 // should really be NET6_0_OR_GREATER
return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are 20 instance of ConstructorDelegate in 7 files. I think we should just replace it with Func<object>.

Assert.NotNull(factory);
StructWithPublicDefaultConstructor instance = factory();
Assert.True(instance.ConstructorInvoked);
Assert.True(IsAddressOnLocalStack(instance.AddressPassedToConstructor)); // using Func<T>, value type ctor is called using ref to stack local, no boxing

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is implementation detail. I can imagine some implementations may choose to allocate the box to keep things simple.


// Then create a delegate to factory.CreateInstance, closed over the "this" parameter.

return (Func<object?>)factory.CreateDelegate((RuntimeType)typeof(Func<object?>));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why can't this just call new Func<object?>(factory.CreateInstance) ? This looks slow and complicated for no good reason.

{
if (typeof(T).IsValueType)
{
IActivationFactory factory = (IActivationFactory)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ActivationFactory<>), (RuntimeType)typeof(T));

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CreateInstanceForAnotherGenericParameter is AOT unfriendly API that depends on JIT. Can this be just something something like this?

class StructFactory<T>
{
T CreateInstance()
{
T value;
_pfnConstructor(&value);
return value;
}
}
return new Func<T>(new StructFactory<T>().CreateInstance);

?

@ViktorHofer

Copy link
Copy Markdown
Member

// Auto-generated message

69e114c which was merged 12/7 removed the intermediate src/coreclr/src/ folder. This PR needs to be updated as it touches files in that directory which causes conflicts.

To update your commits you can use this bash script: https://gist.github.com/ViktorHofer/6d24f62abdcddb518b4966ead5ef3783. Feel free to use the comment section of the gist to improve the script for others.

@ghostghost closed this Jan 25, 2021
@ghost

Copy link
Copy Markdown

Draft Pull Request was automatically closed for inactivity. It can be manually reopened in the next 30 days if the work resumes.

@steveharter

Copy link
Copy Markdown
Contributor

@GrabYourPitchforks based on prior discussion I assume the perf numbers are the same as ref.emit (once warmed up). Is that correct?

@GrabYourPitchforks

GrabYourPitchforks commented Jan 28, 2021

Copy link
Copy Markdown
MemberAuthor

@steveharter Yes, it should meet or beat ref emit in terms of wall clock time + memory utilization.

@ghostghost locked as resolved and limited conversation to collaborators Feb 27, 2021
This pull request was closed.
Sign up for freeto 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.

5 participants

@GrabYourPitchforks@Dotnet-GitSync-Bot@ViktorHofer@steveharter@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Initial prototype of Activator.CreateFactory - #45458

Closed
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance
Closed

Initial prototype of Activator.CreateFactory#45458
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance

Conversation

@GrabYourPitchforks

@GrabYourPitchforksGrabYourPitchforks commented Dec 2, 2020

Copy link
Copy Markdown
Member

** DRAFT ** DRAFT ** DRAFT **

Contributes to #36194. Doesn't fully resolve that issue since there's still a second set APIs which aren't present in this PR.

This is the follow-up to #32520, which takes advantage of that refactoring to introduce new Activator.CreateFactory(Type) and Activator.CreateFactory<T> APIs. Think of a factory as a cached version of CreateInstance, where we've already done all the initial work and can keep invoking our factory over and over every time we need a new instance.

As a reminder, the purpose of this new API is two-fold. First, it allows callers to remove their own usage of ref-emit. Second, it tries to avoid spinning up the JIT as much as possible. Activator.CreateFactory(Type) should be JIT-free in nearly all cases, including for value types. Activator.CreateFactory<T> may spin up the JIT for value types T.

I've also hooked this factory up through System.Text.Json to show how it can replace their existing usage of DynamicMethod in some scenarios.

This is a draft PR and isn't fully ready for review. There's still some code cleanup that needs to take place, but it should give a good indication of how things should look when they're fully finished.

To-do:

  • Make mono runtime compile and tests pass
  • Figure out whether the Delegate changes were appropriate or if they should be backed out
  • Fix layering in Activator.cs and Activator.CoreCLR.cs
  • Fix suppressions and dynamic dependency attributes introduced here
  • Add API docs

There are some other uses of Activator.CreateInstance (see here for one example), but I didn't change those call sites as part of this PR since I don't think they're on hot paths.

/cc @layomia and @jozkee as area owners of System.Text.Json so that they can see how this would look in practice. If you're wondering about the double-delegate pattern, see #45408 and #36194 (comment) for context.

@Dotnet-GitSync-Bot

Copy link
Copy Markdown
Collaborator

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

}

#if NET6_0 // should really be NET6_0_OR_GREATER
return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are 20 instance of ConstructorDelegate in 7 files. I think we should just replace it with Func<object>.

Assert.NotNull(factory);
StructWithPublicDefaultConstructor instance = factory();
Assert.True(instance.ConstructorInvoked);
Assert.True(IsAddressOnLocalStack(instance.AddressPassedToConstructor)); // using Func<T>, value type ctor is called using ref to stack local, no boxing

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is implementation detail. I can imagine some implementations may choose to allocate the box to keep things simple.


// Then create a delegate to factory.CreateInstance, closed over the "this" parameter.

return (Func<object?>)factory.CreateDelegate((RuntimeType)typeof(Func<object?>));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why can't this just call new Func<object?>(factory.CreateInstance) ? This looks slow and complicated for no good reason.

{
if (typeof(T).IsValueType)
{
IActivationFactory factory = (IActivationFactory)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ActivationFactory<>), (RuntimeType)typeof(T));

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CreateInstanceForAnotherGenericParameter is AOT unfriendly API that depends on JIT. Can this be just something something like this?

class StructFactory<T>
{
T CreateInstance()
{
T value;
_pfnConstructor(&value);
return value;
}
}
return new Func<T>(new StructFactory<T>().CreateInstance);

?

@ViktorHofer

Copy link
Copy Markdown
Member

// Auto-generated message

69e114c which was merged 12/7 removed the intermediate src/coreclr/src/ folder. This PR needs to be updated as it touches files in that directory which causes conflicts.

To update your commits you can use this bash script: https://gist.github.com/ViktorHofer/6d24f62abdcddb518b4966ead5ef3783. Feel free to use the comment section of the gist to improve the script for others.

@ghostghost closed this Jan 25, 2021
@ghost

Copy link
Copy Markdown

Draft Pull Request was automatically closed for inactivity. It can be manually reopened in the next 30 days if the work resumes.

@steveharter

Copy link
Copy Markdown
Contributor

@GrabYourPitchforks based on prior discussion I assume the perf numbers are the same as ref.emit (once warmed up). Is that correct?

@GrabYourPitchforks

GrabYourPitchforks commented Jan 28, 2021

Copy link
Copy Markdown
MemberAuthor

@steveharter Yes, it should meet or beat ref emit in terms of wall clock time + memory utilization.

@ghostghost locked as resolved and limited conversation to collaborators Feb 27, 2021
This pull request was closed.
Sign up for freeto 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.

5 participants

@GrabYourPitchforks@Dotnet-GitSync-Bot@ViktorHofer@steveharter@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Initial prototype of Activator.CreateFactory - #45458

Closed
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance
Closed

Initial prototype of Activator.CreateFactory#45458
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance

Conversation

@GrabYourPitchforks

@GrabYourPitchforksGrabYourPitchforks commented Dec 2, 2020

Copy link
Copy Markdown
Member

** DRAFT ** DRAFT ** DRAFT **

Contributes to #36194. Doesn't fully resolve that issue since there's still a second set APIs which aren't present in this PR.

This is the follow-up to #32520, which takes advantage of that refactoring to introduce new Activator.CreateFactory(Type) and Activator.CreateFactory<T> APIs. Think of a factory as a cached version of CreateInstance, where we've already done all the initial work and can keep invoking our factory over and over every time we need a new instance.

As a reminder, the purpose of this new API is two-fold. First, it allows callers to remove their own usage of ref-emit. Second, it tries to avoid spinning up the JIT as much as possible. Activator.CreateFactory(Type) should be JIT-free in nearly all cases, including for value types. Activator.CreateFactory<T> may spin up the JIT for value types T.

I've also hooked this factory up through System.Text.Json to show how it can replace their existing usage of DynamicMethod in some scenarios.

This is a draft PR and isn't fully ready for review. There's still some code cleanup that needs to take place, but it should give a good indication of how things should look when they're fully finished.

To-do:

  • Make mono runtime compile and tests pass
  • Figure out whether the Delegate changes were appropriate or if they should be backed out
  • Fix layering in Activator.cs and Activator.CoreCLR.cs
  • Fix suppressions and dynamic dependency attributes introduced here
  • Add API docs

There are some other uses of Activator.CreateInstance (see here for one example), but I didn't change those call sites as part of this PR since I don't think they're on hot paths.

/cc @layomia and @jozkee as area owners of System.Text.Json so that they can see how this would look in practice. If you're wondering about the double-delegate pattern, see #45408 and #36194 (comment) for context.

@Dotnet-GitSync-Bot

Copy link
Copy Markdown
Collaborator

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

}

#if NET6_0 // should really be NET6_0_OR_GREATER
return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are 20 instance of ConstructorDelegate in 7 files. I think we should just replace it with Func<object>.

Assert.NotNull(factory);
StructWithPublicDefaultConstructor instance = factory();
Assert.True(instance.ConstructorInvoked);
Assert.True(IsAddressOnLocalStack(instance.AddressPassedToConstructor)); // using Func<T>, value type ctor is called using ref to stack local, no boxing

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is implementation detail. I can imagine some implementations may choose to allocate the box to keep things simple.


// Then create a delegate to factory.CreateInstance, closed over the "this" parameter.

return (Func<object?>)factory.CreateDelegate((RuntimeType)typeof(Func<object?>));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why can't this just call new Func<object?>(factory.CreateInstance) ? This looks slow and complicated for no good reason.

{
if (typeof(T).IsValueType)
{
IActivationFactory factory = (IActivationFactory)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ActivationFactory<>), (RuntimeType)typeof(T));

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CreateInstanceForAnotherGenericParameter is AOT unfriendly API that depends on JIT. Can this be just something something like this?

class StructFactory<T>
{
T CreateInstance()
{
T value;
_pfnConstructor(&value);
return value;
}
}
return new Func<T>(new StructFactory<T>().CreateInstance);

?

@ViktorHofer

Copy link
Copy Markdown
Member

// Auto-generated message

69e114c which was merged 12/7 removed the intermediate src/coreclr/src/ folder. This PR needs to be updated as it touches files in that directory which causes conflicts.

To update your commits you can use this bash script: https://gist.github.com/ViktorHofer/6d24f62abdcddb518b4966ead5ef3783. Feel free to use the comment section of the gist to improve the script for others.

@ghostghost closed this Jan 25, 2021
@ghost

Copy link
Copy Markdown

Draft Pull Request was automatically closed for inactivity. It can be manually reopened in the next 30 days if the work resumes.

@steveharter

Copy link
Copy Markdown
Contributor

@GrabYourPitchforks based on prior discussion I assume the perf numbers are the same as ref.emit (once warmed up). Is that correct?

@GrabYourPitchforks

GrabYourPitchforks commented Jan 28, 2021

Copy link
Copy Markdown
MemberAuthor

@steveharter Yes, it should meet or beat ref emit in terms of wall clock time + memory utilization.

@ghostghost locked as resolved and limited conversation to collaborators Feb 27, 2021
This pull request was closed.
Sign up for freeto 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.

5 participants

@GrabYourPitchforks@Dotnet-GitSync-Bot@ViktorHofer@steveharter@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Initial prototype of Activator.CreateFactory - #45458

Closed
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance
Closed

Initial prototype of Activator.CreateFactory#45458
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance

Conversation

@GrabYourPitchforks

@GrabYourPitchforksGrabYourPitchforks commented Dec 2, 2020

Copy link
Copy Markdown
Member

** DRAFT ** DRAFT ** DRAFT **

Contributes to #36194. Doesn't fully resolve that issue since there's still a second set APIs which aren't present in this PR.

This is the follow-up to #32520, which takes advantage of that refactoring to introduce new Activator.CreateFactory(Type) and Activator.CreateFactory<T> APIs. Think of a factory as a cached version of CreateInstance, where we've already done all the initial work and can keep invoking our factory over and over every time we need a new instance.

As a reminder, the purpose of this new API is two-fold. First, it allows callers to remove their own usage of ref-emit. Second, it tries to avoid spinning up the JIT as much as possible. Activator.CreateFactory(Type) should be JIT-free in nearly all cases, including for value types. Activator.CreateFactory<T> may spin up the JIT for value types T.

I've also hooked this factory up through System.Text.Json to show how it can replace their existing usage of DynamicMethod in some scenarios.

This is a draft PR and isn't fully ready for review. There's still some code cleanup that needs to take place, but it should give a good indication of how things should look when they're fully finished.

To-do:

  • Make mono runtime compile and tests pass
  • Figure out whether the Delegate changes were appropriate or if they should be backed out
  • Fix layering in Activator.cs and Activator.CoreCLR.cs
  • Fix suppressions and dynamic dependency attributes introduced here
  • Add API docs

There are some other uses of Activator.CreateInstance (see here for one example), but I didn't change those call sites as part of this PR since I don't think they're on hot paths.

/cc @layomia and @jozkee as area owners of System.Text.Json so that they can see how this would look in practice. If you're wondering about the double-delegate pattern, see #45408 and #36194 (comment) for context.

@Dotnet-GitSync-Bot

Copy link
Copy Markdown
Collaborator

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

}

#if NET6_0 // should really be NET6_0_OR_GREATER
return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are 20 instance of ConstructorDelegate in 7 files. I think we should just replace it with Func<object>.

Assert.NotNull(factory);
StructWithPublicDefaultConstructor instance = factory();
Assert.True(instance.ConstructorInvoked);
Assert.True(IsAddressOnLocalStack(instance.AddressPassedToConstructor)); // using Func<T>, value type ctor is called using ref to stack local, no boxing

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is implementation detail. I can imagine some implementations may choose to allocate the box to keep things simple.


// Then create a delegate to factory.CreateInstance, closed over the "this" parameter.

return (Func<object?>)factory.CreateDelegate((RuntimeType)typeof(Func<object?>));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why can't this just call new Func<object?>(factory.CreateInstance) ? This looks slow and complicated for no good reason.

{
if (typeof(T).IsValueType)
{
IActivationFactory factory = (IActivationFactory)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ActivationFactory<>), (RuntimeType)typeof(T));

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CreateInstanceForAnotherGenericParameter is AOT unfriendly API that depends on JIT. Can this be just something something like this?

class StructFactory<T>
{
T CreateInstance()
{
T value;
_pfnConstructor(&value);
return value;
}
}
return new Func<T>(new StructFactory<T>().CreateInstance);

?

@ViktorHofer

Copy link
Copy Markdown
Member

// Auto-generated message

69e114c which was merged 12/7 removed the intermediate src/coreclr/src/ folder. This PR needs to be updated as it touches files in that directory which causes conflicts.

To update your commits you can use this bash script: https://gist.github.com/ViktorHofer/6d24f62abdcddb518b4966ead5ef3783. Feel free to use the comment section of the gist to improve the script for others.

@ghostghost closed this Jan 25, 2021
@ghost

Copy link
Copy Markdown

Draft Pull Request was automatically closed for inactivity. It can be manually reopened in the next 30 days if the work resumes.

@steveharter

Copy link
Copy Markdown
Contributor

@GrabYourPitchforks based on prior discussion I assume the perf numbers are the same as ref.emit (once warmed up). Is that correct?

@GrabYourPitchforks

GrabYourPitchforks commented Jan 28, 2021

Copy link
Copy Markdown
MemberAuthor

@steveharter Yes, it should meet or beat ref emit in terms of wall clock time + memory utilization.

@ghostghost locked as resolved and limited conversation to collaborators Feb 27, 2021
This pull request was closed.
Sign up for freeto 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.

5 participants

@GrabYourPitchforks@Dotnet-GitSync-Bot@ViktorHofer@steveharter@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Initial prototype of Activator.CreateFactory - #45458

Closed
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance
Closed

Initial prototype of Activator.CreateFactory#45458
GrabYourPitchforks wants to merge 1 commit into
dotnet:masterfrom
GrabYourPitchforks:fast_createinstance

Conversation

@GrabYourPitchforks

@GrabYourPitchforksGrabYourPitchforks commented Dec 2, 2020

Copy link
Copy Markdown
Member

** DRAFT ** DRAFT ** DRAFT **

Contributes to #36194. Doesn't fully resolve that issue since there's still a second set APIs which aren't present in this PR.

This is the follow-up to #32520, which takes advantage of that refactoring to introduce new Activator.CreateFactory(Type) and Activator.CreateFactory<T> APIs. Think of a factory as a cached version of CreateInstance, where we've already done all the initial work and can keep invoking our factory over and over every time we need a new instance.

As a reminder, the purpose of this new API is two-fold. First, it allows callers to remove their own usage of ref-emit. Second, it tries to avoid spinning up the JIT as much as possible. Activator.CreateFactory(Type) should be JIT-free in nearly all cases, including for value types. Activator.CreateFactory<T> may spin up the JIT for value types T.

I've also hooked this factory up through System.Text.Json to show how it can replace their existing usage of DynamicMethod in some scenarios.

This is a draft PR and isn't fully ready for review. There's still some code cleanup that needs to take place, but it should give a good indication of how things should look when they're fully finished.

To-do:

  • Make mono runtime compile and tests pass
  • Figure out whether the Delegate changes were appropriate or if they should be backed out
  • Fix layering in Activator.cs and Activator.CoreCLR.cs
  • Fix suppressions and dynamic dependency attributes introduced here
  • Add API docs

There are some other uses of Activator.CreateInstance (see here for one example), but I didn't change those call sites as part of this PR since I don't think they're on hot paths.

/cc @layomia and @jozkee as area owners of System.Text.Json so that they can see how this would look in practice. If you're wondering about the double-delegate pattern, see #45408 and #36194 (comment) for context.

@Dotnet-GitSync-Bot

Copy link
Copy Markdown
Collaborator

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

}

#if NET6_0 // should really be NET6_0_OR_GREATER
return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are 20 instance of ConstructorDelegate in 7 files. I think we should just replace it with Func<object>.

Assert.NotNull(factory);
StructWithPublicDefaultConstructor instance = factory();
Assert.True(instance.ConstructorInvoked);
Assert.True(IsAddressOnLocalStack(instance.AddressPassedToConstructor)); // using Func<T>, value type ctor is called using ref to stack local, no boxing

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is implementation detail. I can imagine some implementations may choose to allocate the box to keep things simple.


// Then create a delegate to factory.CreateInstance, closed over the "this" parameter.

return (Func<object?>)factory.CreateDelegate((RuntimeType)typeof(Func<object?>));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why can't this just call new Func<object?>(factory.CreateInstance) ? This looks slow and complicated for no good reason.

{
if (typeof(T).IsValueType)
{
IActivationFactory factory = (IActivationFactory)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ActivationFactory<>), (RuntimeType)typeof(T));

@jkotasjkotasDec 2, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CreateInstanceForAnotherGenericParameter is AOT unfriendly API that depends on JIT. Can this be just something something like this?

class StructFactory<T>
{
T CreateInstance()
{
T value;
_pfnConstructor(&value);
return value;
}
}
return new Func<T>(new StructFactory<T>().CreateInstance);

?

@ViktorHofer

Copy link
Copy Markdown
Member

// Auto-generated message

69e114c which was merged 12/7 removed the intermediate src/coreclr/src/ folder. This PR needs to be updated as it touches files in that directory which causes conflicts.

To update your commits you can use this bash script: https://gist.github.com/ViktorHofer/6d24f62abdcddb518b4966ead5ef3783. Feel free to use the comment section of the gist to improve the script for others.

@ghostghost closed this Jan 25, 2021
@ghost

Copy link
Copy Markdown

Draft Pull Request was automatically closed for inactivity. It can be manually reopened in the next 30 days if the work resumes.

@steveharter

Copy link
Copy Markdown
Contributor

@GrabYourPitchforks based on prior discussion I assume the perf numbers are the same as ref.emit (once warmed up). Is that correct?

@GrabYourPitchforks

GrabYourPitchforks commented Jan 28, 2021

Copy link
Copy Markdown
MemberAuthor

@steveharter Yes, it should meet or beat ref emit in terms of wall clock time + memory utilization.

@ghostghost locked as resolved and limited conversation to collaborators Feb 27, 2021
This pull request was closed.
Sign up for freeto 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.

5 participants

@GrabYourPitchforks@Dotnet-GitSync-Bot@ViktorHofer@steveharter@jkotas