Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Fast path IndexOf and variants for ASCII #2630

Merged
merged 1 commit into from
Jan 15, 2016

Conversation

ellismg
Copy link

@ellismg ellismg commented Jan 13, 2016

In ICU doing any sort of index of operation (which includes Prefix and
Suffix checking) is relatively expensive. ICU ends up doing a fair
amount of work and allocations in order to construct a searcher object
which could be reused, but our APIs are not amenable towards working in
this manner.

However, for some cultures we can often fast path ASCII searches when we
know that ASCII and Ordinal comparisions are the same, as is the case
for both Invariant and en-US.

This change has CompareInfo hold some additional state about a locale to
decide if we can do this optimiztion and then wires it up to IndexOf,
LastIndexOf, IsPrefix and IsSuffix.

In the future, we can try to extend the set of allowable cultures that
we preform this optimization on by coming up with better checks on when
it is safe to preform this transformation.

Today, this optimization does not apply when IgnoreSymbols is set,
because we would need to blank some ASCII symbol characters. If this
ends up being a common operation, we could consider having ordinal
implementations that also ignore symbols.

This represents the best that we can do for dotnet/corefx#3672. It gets
us back to where we were before for many common real world cases.

Fixes dotnet/corefx#3672.

@ellismg
Copy link
Author

ellismg commented Jan 13, 2016

/cc @eerhardt @stephentoub

This is not a complete solution to dotnet/corefx#3672 but I believe it's the best we can do given how ICU works.

@ellismg
Copy link
Author

ellismg commented Jan 13, 2016

From the benchmarks:

10 Characters

Old:

            <iteration index="0" Duration="89.144500017166138" />
            <iteration index="1" Duration="90.093300104141235" />
            <iteration index="2" Duration="89.778200030326843" />
            <iteration index="3" Duration="94.651100039482117" />
            <iteration index="4" Duration="87.357599973678589" />
            <iteration index="5" Duration="93.5298000574112" />
            <iteration index="6" Duration="99.903400063514709" />
            <iteration index="7" Duration="91.42549991607666" />
            <iteration index="8" Duration="95.695000052452087" />
            <iteration index="9" Duration="122.16369998455048" />
            <iteration index="10" Duration="94.113599896430969" />

New:

            <iteration index="0" Duration="0.77730000019073486" />
            <iteration index="1" Duration="1.4880000352859497" />
            <iteration index="2" Duration="0.80760002136230469" />
            <iteration index="3" Duration="0.75849997997283936" />
            <iteration index="4" Duration="0.76499998569488525" />
            <iteration index="5" Duration="0.75929999351501465" />
            <iteration index="6" Duration="0.76409995555877686" />
            <iteration index="7" Duration="0.78849995136260986" />
            <iteration index="8" Duration="0.75639998912811279" />
            <iteration index="9" Duration="0.76719999313354492" />
            <iteration index="10" Duration="0.76619994640350342" />

100 Characters

Old:

            <iteration index="0" Duration="154.50300002098083" />
            <iteration index="1" Duration="158.23860001564026" />
            <iteration index="2" Duration="180.84020006656647" />
            <iteration index="3" Duration="158.82889997959137" />
            <iteration index="4" Duration="167.93649995326996" />
            <iteration index="5" Duration="152.82809996604919" />
            <iteration index="6" Duration="150.00400006771088" />
            <iteration index="7" Duration="152.63240003585815" />

New:

            <iteration index="0" Duration="0.8787999153137207" />
            <iteration index="1" Duration="0.82700002193450928" />
            <iteration index="2" Duration="0.8208000659942627" />
            <iteration index="3" Duration="0.82990002632141113" />
            <iteration index="4" Duration="0.91869997978210449" />
            <iteration index="5" Duration="0.89419996738433838" />
            <iteration index="6" Duration="0.91530001163482666" />
            <iteration index="7" Duration="0.910099983215332" />
            <iteration index="8" Duration="0.92519998550415039" />
            <iteration index="9" Duration="0.9934999942779541" />
            <iteration index="10" Duration="2.2657999992370605" />

1000 Characters

Old:

            <iteration index="0" Duration="776.71099996566772" />
            <iteration index="1" Duration="814.99589991569519" />
            <iteration index="2" Duration="808.74610006809235" />

New:

            <iteration index="0" Duration="2.5328000783920288" />
            <iteration index="1" Duration="1.9356999397277832" />
            <iteration index="2" Duration="1.9711999893188477" />
            <iteration index="3" Duration="2.2067999839782715" />
            <iteration index="4" Duration="2.3429000377655029" />
            <iteration index="5" Duration="2.5407000780105591" />
            <iteration index="6" Duration="2.9608999490737915" />
            <iteration index="7" Duration="2.9773000478744507" />
            <iteration index="8" Duration="2.0746001005172729" />
            <iteration index="9" Duration="2.6568000316619873" />
            <iteration index="10" Duration="2.020300030708313" />

@ellismg
Copy link
Author

ellismg commented Jan 13, 2016

@dotnet-bot test CentOS7.1 x64 Release Build please (timeout cloning github)

[SecuritySafeCritical]
internal CompareInfo(CultureInfo culture)
{
m_name = culture.m_name;
m_sortName = culture.SortName;
m_sortHandle = Interop.GlobalizationInterop.GetSortHandle(System.Text.Encoding.UTF8.GetBytes(m_sortName));
m_isAsciiEqualityOrdinal = (m_sortName == "en-US" || m_sortName == "");
Copy link
Member

Choose a reason for hiding this comment

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

Is it just en-US that can do this or all "en" locales?

Copy link
Author

Choose a reason for hiding this comment

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

We can probably do this for all en locales (there are others as well). I will land this and think about ways to extend it (I suspect we could look at the rules for a collator and understand if it's safe to preform this optimization)

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good.

@stephentoub stephentoub changed the title Fast path IndexOf and varaints for ASCII Fast path IndexOf and variants for ASCII Jan 13, 2016
@stephentoub
Copy link
Member

LGTM

In ICU doing any sort of index of operation (which includes Prefix and
Suffix checking) is relatively expensive. ICU ends up doing a fair
amount of work and allocations in order to construct a searcher object
which could be reused, but our APIs are not amenable towards working in
this manner.

However, for some cultures we can often fast path ASCII searches when we
know that ASCII and Ordinal comparisions are the same, as is the case
for both Invariant and en-US.

This change has CompareInfo hold some additional state about a locale to
decide if we can do this optimiztion and then wires it up to IndexOf,
LastIndexOf, IsPrefix and IsSuffix.

In the future, we can try to extend the set of allowable cultures that
we preform this optimization on by coming up with better checks on when
it is safe to preform this transformation.

Today, this optimization does not apply when IgnoreSymbols is set,
because we would need to blank some ASCII symbol characters. If this
ends up being a common operation, we could consider having ordinal
implementations that also ignore symbols.

This represents the best that we can do for dotnet/corefx#3672. It gets
us back to where we were before for many common real world cases.

Fixes dotnet/corefx#3672.
@ellismg ellismg force-pushed the fast-path-index-of branch from 9a46441 to 8c82dd5 Compare January 13, 2016 21:37
@eerhardt
Copy link
Member

LGTM 2

@stephentoub
Copy link
Member

Test Ubuntu x64 Release Build and Test please

(GitHub timeout)

@stephentoub
Copy link
Member

Test Ubuntu x64 Release Build and Test please

stephentoub added a commit that referenced this pull request Jan 15, 2016
Fast path IndexOf and variants for ASCII
@stephentoub stephentoub merged commit f06fa2e into dotnet:master Jan 15, 2016
@stephentoub stephentoub deleted the fast-path-index-of branch January 15, 2016 01:59
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Perf: String.StartsWith Linux regression from 1x speed to 100x speed
6 participants