Skip to content

Porting over some optimizations#10310

Closed
jverral wants to merge 11 commits into
microsoft:mainfrom
jverral:user/joverral/porting_soninnos_opts
Closed

Porting over some optimizations#10310
jverral wants to merge 11 commits into
microsoft:mainfrom
jverral:user/joverral/porting_soninnos_opts

Conversation

@jverral

@jverral jverral commented Nov 11, 2021

Copy link
Copy Markdown
Contributor

Overview

Greatly reduce allocations related to focus/cursors, and MaterialInstance, which was calling renderer.sharedMaterials every frame (which allocates).

Also brings in a keyboard change from the MRTK 2.6 branch into the 2.7 branch.

@keveleigh keveleigh changed the title Porting over Robert Sonnino's optimizations Porting over some optimizations Nov 11, 2021
Comment thread Assets/MRTK/Core/Services/BaseService.cs Outdated
Comment thread Assets/MRTK/Core/AssemblyInfo.cs Outdated
Comment thread Assets/MRTK/Core/Services/BaseEventSystem.cs Outdated
get
{
Debug.Assert(isMarkedDestroyed.HasValue, $"{GetType()} has not set a value for IsMarkedDestroyed, returning false.");
if (!isMarkedDestroyed.HasValue)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does this need the if check if it's also being converted from Debug.Assert to Debug.AssertFormat without an interpolated string? The allocation would be happening because of the interpolated string, but the method call without it will no-op instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe we just need to change anywhere we use interpolated strings in Debug.Assert calls into Debug.AssertFormat calls?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well, there's also GetType() that would still get called. Though we also changed GetType to cache it's result so it's not as expensive, but I use it as an example of Debug.AssertFormat not necessarily being a perfect solution.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

True! A combination of caching the type and using AssertFormat seems good though.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looked into this more...unfortunately since AssertFormat uses params object[] as a parameter, it allocs an array every call :( the if might be our only path forward @davidkline-ms

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh! Caching the type name as an array might get around this...digging in!

image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, that dropped me to 0 allocs without the if:
image
I also refactored the strings out into const strings, since we're no longer interpolating them

Comment thread Assets/MRTK/Core/Utilities/DebugUtilities.cs
Comment thread Assets/MRTK/Core/Version.txt Outdated
Comment thread Assets/MRTK/Core/Utilities/Gltf/Serialization/GltfUtility.cs
switch (state)
{
case KeyboardState.Showing:
if (TouchScreenKeyboard.visible == true)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this might bust some OS versions where the .visible state isn't properly reported. That's a main reason why we wrote all this custom keyboard code in the first place. @MaxWang-MS might be able to elaborate.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for pointing this out Kurtis! It is a known Unity issue that the .visible state can be inaccurate under certain circumstances, even on the latest HL2 OS. So we deliberately decided to track the state ourselves in this class instead of relying on that Unity property.

Comment thread Assets/MRTK/Services/InputSystem/FocusProvider.cs
if (pointersList[i].Pointer is T typePointer && !typePointer.IsNull())
{
typePointers.Add(typePointer);
yield return typePointer;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We discussed this change a while back in #8118. Any chance you did any perf measurements to verify that this isn't trading garbage for perf?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Honestly, the best solution would be to have an overload where you can pass a list in, but that would be a breaking change. The comparison in the article is not exactly valid because here you were creating a List, iterating on it with foreach and returning the IEnumerable anyway.

@keveleigh

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@keveleigh

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@david-c-kline david-c-kline left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

there are some issues in this pr that should be addressed (ex: replace assert with logerror, fix calling of AssetsContainsShaders)

Comment thread Assets/MRTK/Core/Services/BaseService.cs Outdated
if (!AssetsContainsShaders(packageShaderFolder))
DirectoryInfo packageShaderFolder = FindShaderFolderInPackage();

if (!AssetsContainsShaders(packageShaderFolder, sentinelPath, ignoreFile))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I dont see changes to AssetsContainsShaders() that reflect the new parameter list.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just pushed this change!

get
{
Debug.Assert(isMarkedDestroyed.HasValue, $"{GetType()} has not set a value for IsMarkedDestroyed, returning false.");
if (!isMarkedDestroyed.HasValue)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looked into this more...unfortunately since AssertFormat uses params object[] as a parameter, it allocs an array every call :( the if might be our only path forward @davidkline-ms

get
{
Debug.Assert(isMarkedDestroyed.HasValue, $"{GetType()} has not set a value for IsMarkedDestroyed, returning false.");
if (!isMarkedDestroyed.HasValue)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh! Caching the type name as an array might get around this...digging in!

image

get
{
Debug.Assert(isMarkedDestroyed.HasValue, $"{GetType()} has not set a value for IsMarkedDestroyed, returning false.");
if (!isMarkedDestroyed.HasValue)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, that dropped me to 0 allocs without the if:
image
I also refactored the strings out into const strings, since we're no longer interpolating them

/// <typeparam name="T">The type of pointers to request. Use IMixedRealityPointer to access all pointers.</typeparam>
/// <param name="predicate">A function that returns true when the parameter satisfies a condition.</param>
/// <returns>The first pointer in the collection that satisfies the specified condition.</returns>
T GetFirstPointerWhere<T>(System.Predicate<T> predicate) where T : class, IMixedRealityPointer;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if we can approach these as extension methods instead. Changing this interface would be a breaking change for anybody who has rolled their own focus provider, and I know of a handful of people who have.

{
var spherePointers = focusProvider.GetPointers<SpherePointer>();
foreach (var spherePointer in spherePointers)
var spherePointer = focusProvider.GetFirstPointerWhere<SpherePointer>(p => p.Controller == Pointer.Controller);

@keveleigh keveleigh Nov 11, 2021

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure this change does a whole ton to the allocations here. It seems to only drop it from 144B to 112B (which is some decrease for sure!). I'm pretty sure the new allocs are from the predicate being passed into GetFirstPointerWhere. I'll investigate.

Before this change:
image

After:
image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Okay I reworked this to avoid the alloc!
image
It's a kinda-weird static pattern that I've used in the past, but it should work!

@keveleigh

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

keveleigh added a commit to keveleigh/HoloToolkit-Unity that referenced this pull request Nov 13, 2021
This is semi-ported out of microsoft#10310
keveleigh added a commit to keveleigh/HoloToolkit-Unity that referenced this pull request Nov 13, 2021
This is semi-ported out of microsoft#10310
keveleigh added a commit to keveleigh/HoloToolkit-Unity that referenced this pull request Nov 13, 2021
This is semi-ported out of microsoft#10310
@david-c-kline

Copy link
Copy Markdown

@keveleigh, assigning to you to continue cherry picking the key fixes

@david-c-kline

Copy link
Copy Markdown

@keveleigh is reviewing this PR. he will work with @jverral

@david-c-kline

Copy link
Copy Markdown

Closing as the key improvements have been ingested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants