Quantcast
Channel: Category Name
Viewing all 5971 articles
Browse latest View live

Helping Customers Effectively

$
0
0

I have to put a disclaimer here since this is not the usual type of blog posts I write. I’m by no means a master at communication. This is just what I thought that seemed to work well. YMMV of course. But I’d be very happy if they help you in some way ‘cause many of us work with customers. And I welcome your thoughts and suggestions.

I have talked to customers a lot since I started working on the GC. This is in the context of helping customers to handle and resolve issues they are seeing.

1. The intention of helping should be genuine

I am naturally the kind of person who wants to help with coming up with a solution when I see a problematic situation. But an equally important reason in the professional context is that I think that it’s actually a bit misleading to say “help customers” ‘cause it makes it sound like “I’m the giver and the customer is the receiver”. In reality the end result of helping customers is largely to help ourselves ‘cause we wouldn’t be here if we didn’t have customers 😀. If you think about this, the empathy for customers will come naturally and you will genuinely want to help.

I’m sure we all have had this experience – when you sense someone doesn’t actually want to help you, it’s never a pleasant experience.

2. Respect others and assume the best intentions

Respect is a 2-way street so this is really a suggestion for all parties involved.

I have definitely come across my share of customers who simply didn’t make it easy for themselves to be helped. I’ve worked with people who were hostile and/or demeaning and/or didn’t care to make any effort to try any solutions and only continued to complain. But most folks I’ve worked with were very reasonable. Some were so accommodating and that I’m really grateful that I have customers like them. By default I always treat someone new I talk to with respect. And I never felt the need to change this default. I always assume that there’s a good reason why they have or have not done something.

When I ask someone for help, I’m always appreciative – this doesn’t just mean to say “I appreciate your help”, much more importantly, having a clear description of the issue and showing them what you have already tried shows that you have respect for their time. It makes people much less interested to help you when they need to pull the info out of you. We’ve probably all seen the cases where someone is like “It’s broken. Can you look at it?” with no description of what they are doing, what version of the product they are using, whether they have collected dumps/traces and whether they have done any analysis at all and when you ask them they give minimal answers.

I’m actually totally fine with it when people come to me but haven’t done much investigation on their own, as long as they are willing to do it if it helps to resolve the issue. I understand that people have different experiences – there are hardcore perf folks who do memory analysis on a daily basis – honestly I’m impressed with the level of investigation and understanding from some customers I’ve worked with and deeply appreciative of them. And there are folks who just haven’t had the need to learn about memory and that’s fine (in a way this is a good sign – it means GC and our libraries work well enough that they don’t need to care). Sometimes this actually shows a problem on our side – it may mean our doc is too hard to discover or our explanation is simply insufficient. Over the years I’ve put in a lot of effort in improving the tooling and the documentation so it’s easier for the customers to discover and diagnose problems on their own.

If I have any doubts about the feasibility of my suggestions I would end the conversation with “Does this sound reasonable to you?” which provides a clear opportunity for them to voice their concerns. I myself find that very helpful when other people say it to me so I’d like to believe it’s helpful when I say it to my customers.

3. Understand the problem before suggesting solutions

This sounds obvious but you’d be surprised how often people would jump ahead of themselves and talk about solutions without much understanding of the problem they are trying to solve. It’s interesting ‘cause I’ve seen this on both sides. Some of us would start spelling out various solutions that may be useful or just enumerate solutions they’ve tried and worked before; and some customers would want suggestions before they explain what the issues they are facing actually are.

Understanding the problem often means getting data or sometimes even trying out a prototype in order to get data. And I completely understand that may not be feasible so there’s definitely a time and place that calls for “hey, I’ve worked on this for a while and have seen many cases, and here are the possible causes/solutions you should try”. But I’ve definitely seen this happen way before it’s concluded that’s not feasible or without even thinking about the feasibility at all.

When I request info from folks, I explain why I’m requesting it, ie, what kind of things it would help me figure out so we can make forward progress. This helps with justifying the time they’ll need to spend to get the info.

4. Convey applicable information

When I describe the solution/workaround, I include 2 pieces of info – the diagnostics I did, in a way that’s relevant to the issue and the action items for the customer and/or for us.

I’ve seen devs write long emails that mentioned a lot of info when responding to an issue, yet it’s not obvious how each piece of info is related to the issue at hand and more importantly, not obvious what the action items are. Most of the info may not be important for the customer to understand at all to resolve the issue and especially when folks are under time pressure (which means they are busier than usual), this doesn’t exactly help with the situation.

I have a coworker who does this exceptionally well – he would describe the root cause and the fix/workaround at the beginning of his email in a concise yet informative way, and specifically say “the rest of the email is the details that you only need to go read if you are curious”. And the rest of his email describes things in very much detail.

—–

Of course, as mentioned in the beginning, this is all in the context of helping to resolve issues. If you are helping customers in a different context, eg. “I’m writing a blog post to tell my customers some info about my product that they may not need in their daily work but just ‘cause they are a curious bunch” then of course some of this does not apply.

The post Helping Customers Effectively appeared first on .NET Blog.


Extending the power of Azure AI to Microsoft 365 users

$
0
0

Today, Yusuf Mehdi, Corporate Vice President of Modern Life and Devices, announced the availability of new Microsoft 365 Personal and Family subscriptions. In his blog, he shared a few examples of how Microsoft 365 is innovating to deliver experiences powered by artificial intelligence (AI) to billions of users every day. Whether through familiar products like Outlook and PowerPoint, or through new offerings such as Presenter Coach and Microsoft Editor across Word, Outlook, and the web, Microsoft 365 relies on Azure AI to offer new capabilities that make their users even more productive.

What is Azure AI?

Azure AI is a set of AI services built on Microsoft’s breakthrough innovation from decades of world-class research in vision, speech, language processing, and custom machine learning. What is particularly exciting is that Azure AI provides our customers with access to the same proven AI capabilities that power Microsoft 365, Xbox, HoloLens, and Bing. In fact, there are more than 20,000 active paying customers—and more than 85 percent of the Fortune 100 companies have used Azure AI in the last 12 months.

Azure AI helps organizations:

  • Develop machine learning models that can help with scenarios such as demand forecasting, recommendations, or fraud detection using Azure Machine Learning.
  • Incorporate vision, speech, and language understanding capabilities into AI applications and bots, with Azure Cognitive Services and Azure Bot Service.
  • Build knowledge-mining solutions to make better use of untapped information in their content and documents using Azure Search.

Microsoft 365 provides innovative product experiences with Azure AI

The announcement of Microsoft Editor is one example of innovation. Editor, your personal intelligent writing assistant is available across Word, Outlook.com, and browser extensions for Edge and Chrome. Editor is an AI-powered service available in more than 20 languages that has traditionally helped writers with spell check and grammar recommendations. Powered by AI models built with Azure Machine Learning, Editor can now recommend clear and concise phrasing, suggest more formal language, and provide citation recommendations.

Screen shot of Editor within Microsoft Word helping provide insights like readability, count of distinct words, time to read, and time to speak.

Additionally, Microsoft PowerPoint utilizes Azure AI in multiple ways. PowerPoint Designer uses Azure Machine Learning to recommend design layouts to users based on the content on the slide. In the example image below, Designer made the design recommendation based on the context in the slide. It can also can intelligently crop objects and people in images and place them in optimal layout on a slide. Since its launch, PowerPoint Designer users have kept nearly two billion Designer slides in their presentation.

You can take a closer look at how the PowerPoint team built this feature with Azure Machine Learning in this blog.

PowerPoint slide depicting an example of a recommended slide layout, adding icons that correspond to each animal in the list.

PowerPoint also uses Azure Cognitive Services such as the Speech service to power live captions and subtitles for presentations in real-time, making it easier for all audience members to follow along. Additionally, PowerPoint also uses Translator Text to provide live translations into over 60 languages to reach an even wider audience. These AI-powered capabilities in PowerPoint are providing new experiences for users, allowing them to connect with diverse audiences they were unable to reach before.

These same innovations can also be found in Microsoft Teams. As we look to stay connected with co-workers, Teams has some helpful capabilities intended to make it easier to collaborate and communicate while working remotely. For example, Teams offers the ability of live captioning meetings, which leverages the Speech API for speech transcription. But it doesn’t stop there. As you saw with PowerPoint, Teams also uses Azure AI for live translations when you set up Live Events. This functionality is particularly useful for company town hall meetings or even for any virtual event with up to ten thousand attendees, allowing presenters to reach audiences worldwide

Teams live translation

These are just a few of the ways Microsoft 365 applications utilize Azure AI to deliver industry-leading experiences to billions of users. When you consider the fact that other Microsoft products such as Microsoft 365, Xbox, HoloLens 2, Dynamics 365, and Power Platform all rely on Azure AI, you begin to see the massive scale and the breadth of scenarios that only Azure can offer. Best of all, these same capabilities are available to anyone in Azure AI. 

Introducing the new Microsoft 365 Personal and Family subscriptions

New Microsoft 365 offerings for small and medium-sized businesses

Porting a C++/CLI Project to .NET Core

$
0
0

One of the new features of Visual Studio 2019 (beginning with version 16.4) and .NET Core 3.1 is the ability to build C++/CLI projects targeting .NET Core. This can be done either directly with cl.exe and link.exe (using the new /clr:netcore option) or via MSBuild (using <CLRSupport>NetCore</CLRSupport>). In this post, I’ll walk through the steps necessary to migrate a simple C++/CLI interop project to .NET Core. More details can be found in .NET Core documentation.

The sample project

First, I need to make a sample solution to migrate. I’m going to use an app with a native entry point that displays a Windows Forms form via C++/CLI. Migrating a solution with a managed entry point interoperating with native dependencies via C++/CLI would be just as easy, though. To get started, I’ve created a solution with three projects:

  1. NativeApp. A C++ Windows app from Visual Studio’s ‘Windows Desktop Application’ template.
    1. This will be they app’s entry point.
    2. I’ve updated it to display the managed form (via the CppCliInterop project) and call a method on it when the IDM_ABOUT command is invoked.
  2. ManagedLibrary. A C# Windows Forms library targeting .NET Core.
    1. This will provide a WinForms form for the native app to display.
    2. I’ve added a text box to the form and a method to set the text box’s text. I’ve also multi-targeted this project for .NET Core and .NET Framework so that it can be used with either. This way we can focus on migrating just the C++/CLI portion of the sample.
  3. CppCliInterop. A .NET Framework C++/CLI Library.
      1. This will be used as the interop layer to connect the app to the managed WinForms library.
      2. It references ManagedLibrary and allows native projects to use it.
      3. This is the project that needs to be migrated to .NET Core.

The sample code is available on GitHub. When you start the app, if you click on the Help -> About menu, the WinForms form will be displayed with text in its text box supplied by the NativeApp project.

Migrating a vcxproj to .NET Core

Now for the interesting part – updating the sample app to run on .NET Core. The changes needed are actually quite minimal. If you’ve migrated C# projects to .NET Core before, migrating C++/CLI projects is even simpler because the project file format doesn’t change. With managed projects, .NET Core and .NET Standard projects use the new SDK-style project file format. For C++/CLI projects, though, the same vcxproj format is used to target .NET Core as .NET Framework.

All that’s needed is to make a few changes to the project file. Some of these can be done through the Visual Studio IDE, but others (such as adding WinForms references) can’t be yet. So the easiest way to update the project file, currently, is to just unload the project in VS and edit the vcxproj directly or to use an editor like VS Code or Notepad.

  1. Replace <CLRSupport>true</CLRSupport> with <CLRSupport>NetCore</CLRSupport>. This tells the compiler to use /clr:netcore instead of /clr when building.
    1. This change can be done through Visual Studio’s project configuration interface if you prefer.
    2. Note that <CLRSupport> is specified separately in each configuration/platform-specific property group in the sample project’s project file, so the update needs to be made four different places.
  2. Replace <TargetFrameworkVersion>4.7</TargetFrameworkVersion> with <TargetFramework>netcoreapp3.1</TargetFramework>.
    1. These settings can be modified through Visual Studio’s project configuration interface in the ‘Advanced’ tab. Note, however, that changing a project’s CLR support setting as described in the previous step won’t change <TargetFrameworkVersion> automatically, so be sure to clear the “.NET Target Framework Version” setting before selecting .NET Core Runtime Support.
  3. Replace .NET Framework references (to System, System.Data, System.Windows.Forms, and System.Xml) with the following reference to WinForms components from the Windows Desktop .NET Core SDK. This step doesn’t have Visual Studio IDE support yet, so it must be done by editing the vcxproj directly. Notice that only a reference to the Windows Desktop SDK is needed because the .NET Core SDK (which includes libraries like System, System.Xml, etc.) is included automatically. There are different Framework references for WinForms, WPF, or both (as explained in the migration docs).
    1. <FrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms" />

With those changes made, the C++/CLI project will build successfully targeting .NET Core. If you’re using the latest version of Visual Studio 2019 (16.5 or 16.6 preview 1), everything should work at runtime, too, and the migration is done!

Prior to Visual Studio 2019 16.5 preview 2, C++/CLI libraries didn’t generate the .runtimeconfig.json file necessary for C++/CLI libraries to indicate which version of .NET Core they use, so it had to be added manually. So, if you’re using an older version of Visual Studio, you’ll need to create this CppCliInterop.runtimeconfig.json file manually and make sure it’s copied to the output directory:

{
  "runtimeOptions": {
    "tfm": "netcoreapp3.1",
    "framework": {
      "name": "Microsoft.WindowsDesktop.App",
      "version": "3.1.0"
    }
  }
}

The app can now run on .NET Core! A migrated version of the source is available in the NetCore branch in the sample’s GitHub repository. Here’s the Windows form running in front of the loaded modules showing coreclr.dll loaded.

C++/CLI scenario on .NET Core

Building without MSBuild

Migrating this sample app to .NET Core was simply a matter of updating the project file to target .NET Core instead of .NET Framework. If you need to build C++/CLI assemblies directly with cl.exe and link.exe, that’s supported, too. The necessary steps are:

  1. Use /clr:netcore in place of /clr when calling cl.exe.
  2. Reference necessary .NET Core reference assemblies using /FU (.NET Core reference assemblies are typically installed under %ProgramFiles%dotnetpacks<SDK><Version>ref).
  3. When linking, include the .NET Core app host directory as a LibPath. The .NET Core app host files are typically installed under %ProgramFiles%dotnetpacksMicrosoft.NETCore.App.Host.win-x64<Version>runtimewin-x64native).
  4. Make sure that ijwhost.dll (which is needed to start the .NET Core runtime) is copied locally from the .NET Core app host location. MSBuild does this automatically if building a vcxproj project.
  5. Create a .runtimeconfig.json file, as discussed previously.

A few caveats

As you can see, with Visual Studio 2019 and .NET Core 3.1, targeting .NET Core with C++/CLI projects is easy. There are a few C++/CLI limitations to look out for, though.

  1. C++/CLI support is Windows only, even when running on .NET Core. If you need interoperability cross-platform, use platform invokes.
  2. C++/CLI projects cannot target .NET Standard – only .NET Core or .NET Framework – and multi-targeting isn’t supported, so building a library that will be used by both .NET Framework and .NET Core callers will require two project files.
  3. If a project uses APIs that aren’t available in .NET Core, those calls will need to be updated to .NET Core alternatives. The .NET Portability Analyzer can help to find any Framework dependencies that won’t work on .NET Core.

Wrap-up and resources

Hopefully this sample shows how to take advantage of the new functionality in Visual Studio 2019 and .NET Core 3.1 to migrate C++/CLI projects to .NET Core. The following links may be useful for further reading.

 

The post Porting a C++/CLI Project to .NET Core appeared first on C++ Team Blog.

.NET for Apache® Spark™ In-Memory DataFrame Support

$
0
0

.NET for Apache Spark is aimed at making Apache® Spark™, and thus the exciting world of big data analytics, accessible to .NET developers. .NET for Spark can be used for processing batches of data, real-time streams, machine learning, and ad-hoc query.

The DataFrame is one of the core data structures in Spark programming. A DataFrame is a distributed collection of data organized into named columns. In a Spark application, we typically start off by reading input data from a data source, storing it in a DataFrame, and then leveraging functionality like Spark SQL to transform and gain insights from our data. User-defined functions, or UDFs, are column-based functions that allow us to manipulate data stored in DataFrames.

In December 2019, the .NET team announced the preview of the Microsoft.Data.Analysis.DataFrame type to make data exploration easy in .NET. Now in March 2020, we have introduced convenience APIs to the .NET for Spark codebase for using Microsoft.Data.Analysis.DataFrame objects with UDFs in Spark. These convenience APIs make data manipulation and analysis with UDFs much more convenient and concise in .NET for Spark.

In this blog post, we’ll explore:

Implementation goals and details

Context and Goals

Let’s start off with some context about data-sharing in Spark UDFs. Apache Spark streams data to Arrow-based UDFs in the Apache Arrow format. Apache Arrow provides a standardized, language-independent format for working with data in-memory. It’s designed for high-performance, efficient analysis through its columnar memory format, and it provides libraries and zero-copy messaging for communication across processes.

Diagram of the benefits of Apache Arrow

Diagram credit: https://arrow.apache.org/.

Because Spark streams data to UDFs in the Arrow format, you need to understand the Arrow format when working with UDFs, such as how to read Arrow columns, write to Arrow columns, and unwrap a RecordBatch, which is a 2D data type in Arrow consisting of a set of rows of equal-length columns.

The main goal of the work described in this blog post is to improve scalar and vector UDFs in .NET for Spark through a set of convenience APIs. You can now use Microsoft.Data.Analysis.DataFrame objects in your .NET for Spark apps, which take care of working with Arrow-formatted data for you behind-the-scenes of your UDFs.

Details

There are a few kinds of Spark UDFs: pickling, scalar, and vector. Our convenience APIs specifically apply to scalar and vector UDFs.

Python pickling UDFs are an older version of Spark UDFs. They leverage the Python pickling format of serialization, rather than Arrow, to convert data between the JVM and .NET for Spark processes. Once a column is specified for processing, a pickling UDF will take each of its rows, apply the given functionality, and then add a new column, resulting in quite a bit of overhead.

By contrast, scalar and vector UDFs leverage Arrow serialization, enabling them to reap the benefits of an in-memory columnar format and making data transfers more efficient. Once data is serialized to the Arrow format, it can be used directly in the Spark processes and doesn’t need to be serialized or deserialized anymore, which is a major improvement over the Python pickling format. Arrow can create DataFrames using zero-copy methods across chunks of data (multiple rows and columns all at once) rather than row-by-row.

Our new .NET for Apache Spark convenience APIs specifically apply to scalar and vector UDFs since they use Arrow.

Prior to these convenience APIs, you needed to enumerate an Arrow RecordBatch to work with columns in an Arrow-based UDF. RecordBatches are Arrow-based objects, not standard Spark objects, and can thus disrupt the flow or familiarity of code in your program.

Our new APIs automatically wrap data in a Microsoft.Data.Analysis.DataFrame instead of a RecordBatch. The wrapping doesn’t involve copying data, thus ensuring performance remains high as our ease of coding also improves.

You can use both traditional Spark SQL and Microsoft.Data.Analysis.DataFrames in your programs. The traditional Spark DataFrame distributes data across your Spark cluster. It’s used for the entire dataset in your Spark driver program. Once you create a UDF, the data in the traditional DataFrame will be streamed to the UDF on the worker machines in the Arrow format. Once inside the UDF, you’ll now work with the Microsoft.Data.Analysis.DataFrame (rather than RecordBatches)- it will be in-memory on a single machine. The concept of the Microsoft.Data.Analysis.DataFrame is similar to the Python Pandas DataFrame.

Example

Simple Examples

Let’s start with a basic example. Suppose we have a vector UDF that adds 2 columns and returns the result. Traditionally, the UDF would take in 2 ArrowArrays (for example, DoubleArray) and return a new ArrowArray. You would unfortunately need to create the new array, and then loop over each row in the data. In addition to the loop, you’d also need to deal with Builder objects in the Arrow library to create the new array, resulting in more allocations than necessary.

If we instead used the Microsoft.Data.Analysis.DataFrame, we can write something along the lines of: dataframe.ColumnA + dataframe.ColumnB. Isn’t that convenient!

As another example, we often create UDFs that return a set of columns. With the traditional Spark DataFrames, these columns must be returned as an Arrow RecordBatch. But with our new convenience APIs, we can just return a DataFrame, and everything else is handled internally!

Detailed Examples

Vector Udfs

Let’s take a look at a more detailed, concrete example. VectorUdfs.cs is a program using the traditional Spark DataFrame. It reads in a Json file with people’s names and ages as input and stores the data in a DataFrame. The program leverages Spark to group records by the same age, and then applies a custom UDF over each age group.

Let’s say you have 2 people with the same age:

21 | John
21 | Sally

The UDF will count the number of characters in all the names that have the same age. So, in this case, you’d get 1 row back: 21 | 9, where 9 is the result of “John”.Length + “Sally”.Length.

VectorDataFrameUdfs.cs is an updated program that accomplishes the same task with both a traditional DataFrame and the Microsoft.Data.Analysis.DataFrame.

Both programs use a Grouped Map Vector UDF and apply it very similarly. In VectorUdfs.cs, the code is as follows:

df.GroupBy("age")
    .Apply(
    new StructType(new[]
    {
        new StructField("age", new IntegerType()),
        new StructField("nameCharCount", new IntegerType())
    }),
r => CountCharacters(r))

CountCharacters is implemented differently in each program. In VectorUdfs.cs, the definition is:

private static RecordBatch CountCharacters(RecordBatch records)
{
    StringArray nameColumn = records.Column("name") as StringArray;

    int characterCount = 0;

    for (int i = 0; i < nameColumn.Length; ++i)
    {
        string current = nameColumn.GetString(i);
        characterCount += current.Length;
    }

    int ageFieldIndex = records.Schema.GetFieldIndex("age");
    Field ageField = records.Schema.GetFieldByIndex(ageFieldIndex);

    // Return 1 record, if we were given any. 0, otherwise.
    int returnLength = records.Length > 0 ? 1 : 0;

    return new RecordBatch(
        new Schema.Builder()
            .Field(ageField)
            .Field(f => f.Name("name_CharCount").DataType(Int32Type.Default))
            .Build(),
        new IArrowArray[]
        {
            records.Column(ageFieldIndex),
            new Int32Array.Builder().Append(characterCount).Build()
        },
        returnLength);
}

In VectorDataFrameUdfs.cs, the method is:

private static FxDataFrame CountCharacters(FxDataFrame dataFrame)
{
    int characterCount = 0;

    var characterCountColumn = new PrimitiveDataFrameColumn<int>("nameCharCount");
    var ageColumn = new PrimitiveDataFrameColumn</int><int>("age");
    ArrowStringDataFrameColumn nameColumn = dataFrame["name"] as ArrowStringDataFrameColumn;
    for (long i = 0; i < dataFrame.Rows.Count; ++i)
    {
        characterCount += nameColumn[i].Length;
    }

    if (dataFrame.Rows.Count > 0)
    {
        characterCountColumn.Append(characterCount);
        ageColumn.Append((int?)dataFrame["age"][0]);
    }

    return new FxDataFrame(ageColumn, characterCountColumn);
}
</int>

Note that the FxDataFrame type represents the Microsoft.Data.Analysis.DataFrame, while DataFrame represents the traditional Spark DataFrame. This is signified in the latter sample at the top of the program:

using DataFrame = Microsoft.Spark.Sql.DataFrame;
using FxDataFrame = Microsoft.Data.Analysis.DataFrame;

As you can see, the latter CountCharacters implementation deals completely with DataFrames rather than RecordBatches. It is also almost half the length of the first implementation!

In this single comparison, we can see where these new APIs add a great deal of convenience to our .NET for Spark apps. VectorUdfs.cs requires us to convert to and from the RecordBatch type, requiring many extra lines of code. By contrast, the new VectorDataFrameUdfs.cs sample can dive immediately into our data processing.

TPC-H Vector Functions

.NET for Apache Spark is designed for high performance and performs well on the TPC-H benchmark. The TPC-H benchmark consists of a suite of business-oriented ad hoc queries and concurrent data modifications. The queries and the data populating the database have been chosen to have broad industry-wide relevance.

VectorFunctions.cs and VectorDataFrameFunctions.cs both perform the same ComputeTotal TPC-H function where prices are calculated based on taxes and discounts. However, only the latter program leverages Microsoft.Data.Analysis.DataFrame.

ComputeTotal in VectorFunctions.cs:

internal static DoubleArray ComputeTotal(DoubleArray price, DoubleArray discount, DoubleArray tax)
{
    if ((price.Length != discount.Length) || (price.Length != tax.Length))
    {
        throw new ArgumentException("Arrays need to be the same length");
    }

    int length = price.Length;
    var builder = new DoubleArray.Builder().Reserve(length);
    ReadOnlySpan<double> prices = price.Values;
    ReadOnlySpan</double><double> discounts = discount.Values;
    ReadOnlySpan</double><double> taxes = tax.Values;
    for (int i = 0; i < length; ++i)
    {
        builder.Append(prices[i] * (1 - discounts[i]) * (1 + taxes[i]));
    }

    return builder.Build();
}
</double>

Whereas the logic in ComputeTotal in VectorDataFrameFunctions.cs (not including the initial array length check) is just 1 line!

return price * (1 - discount) * (1 + tax);

Now we can harness the tremendous benefits of Apache Arrow without extra code overhead or confusion – awesome!

Wrap Up

Thank you to Prashanth Govindarajan, Eric Erhardt, Terry Kim, and the other members of the .NET and .NET for Apache Spark teams for their contributions to this outstanding work.

We’d love to help you get started with .NET for Apache Spark and hear your feedback. You can Request a Demo from our landing page and check out the .NET for Spark GitHub repo to get involved with our effort to make .NET a great tech stack for building big data applications.

The post .NET for Apache® Spark™ In-Memory DataFrame Support appeared first on .NET Blog.

Improved Git Experience in Visual Studio 2019

$
0
0

Last week we released version 16.6 Preview 2 of Visual Studio 2019. It contained the first iteration of a revamped Git experience to improve your productivity when working with code on GitHub, Azure Repos, and other hosting services.

You can enable or disable the experience by searching (Ctrl+Q) for preview features. In the Options window, just toggle the checkbox for the New Git user experience. We acknowledge that the functionality is still incomplete, with more enhancements coming soon. But we do expect this to be the default experience in the future. So in the meantime, we’re depending on you, the community, to let us know what we should prioritize in order to build what you need.

Turn on new Git UX preview feature

Turning on the new Git user experience in Preview Features

Initialize and Push

You can now initialize a local Git repository and push it directly to GitHub, Azure Repos, or other remote hosting services (e.g. BitBucket, custom Git servers, etc.) with a single click. If you have an existing project online, you can use the built-in GitHub and Azure Repos browsing experiences to clone your code.

Initialize and push a repository to GitHub

Initializing and pushing a repository to GitHub

Create new branches

Once your repository is initialized, we want to enable you to focus on your daily development workflows without having to leave your code. You can create branches and commit code changes from the new Git menu and the Git tool window.

Create a branch and commit changes

Creating a branch and committing changes

Manage branches

Context switching between tools and applications can be a pain. So we’ve added the ability to manage your branches from within the Git tool window. After working on your new feature or bug fix, use the branch dropdown in the Git tool window to check out, merge, rebase, view history, rename, and delete your branches.

Merging and deleting a branch

Merging a branch and deleting it

Resolve merge conflicts

We understand collaborating with your team and sharing your work is very important, especially so in the current climate with increased remote work. When it comes to keeping your code up to date, this can be done easily using the fetch, pull, and push shortcuts in the Git tool window. But even when you do your best to stay in sync with the latest code changes, running into merge conflicts is sometimes inevitable. With the improved experience, we’ve started to make it easier to navigate through and resolve your merge conflicts.

Resolve a merge conflict

Resolving a merge conflict

Please Share Your Feedback

This is just the beginning of a new first-class Git and GitHub experience in Visual Studio. Please add or vote for suggestions on the most important functionality that you want us to build or change.

Also be sure to keep these reference images handy for a quick overview of the new Git interface.

Image Git Tool WindowImage Git Menu

And finally – stay safe, stay healthy.

The post Improved Git Experience in Visual Studio 2019 appeared first on Visual Studio Blog.

Microsoft partners with the industry to unlock new 5G scenarios with Azure Edge Zones

$
0
0

Cloud, edge computing, and IoT are making strides to transform whole industries and create opportunities that weren't possible just a few years ago. With the rise of 5G mobile connectivity, there are even more possibilities to deliver immersive, real-time experiences that have demanding, ultra-low latency, and connectivity requirements. 5G opens new frontiers with enhanced mobile broadband up to 10x faster, reliable low-latency communication, and very high device density up to 1 million devices per square kilometer.

Today we’re announcing transformative advances to combine the power of Azure, 5G, carriers, and technology partners around the world to enable new scenarios for developers, customers, and partners, with the preview of Azure Edge Zones.

New 5G customer scenarios with Azure Edge Zones

Azure Edge Zones and Azure Private Edge Zones deliver consistent Azure services, app platform, and management to the edge with 5G unlocking new scenarios by enabling:

  • Development of distributed applications across cloud, on-premises, and edge using the same Azure Portal, APIs, development, and security tools.
  • Local data processing for latency critical industrial IoT and media services workloads.
  • Acceleration of IoT, artificial intelligence (AI), and real-time analytics by optimizing, building, and innovating for robotics, automation, and mixed reality.
  • New frontiers for developers working with high-density graphics and real-time operations in industries such as gaming.
  • An evolving platform built with customers, carriers, and industry partners to allow seamless integration and operation of a wide selection of Virtual Network Functions, including 5G software and SD-WAN and firewalls from technology partners such as Affirmed, Mavenir, Nuage Networks from Nokia, Metaswitch, Palo Alto, and VeloCloud By VMware.

Edge Zones 1a

Building on our previous work with AT&T, we’re announcing the preview of Azure Edge Zones with carriers, connecting Azure services directly to 5G networks in the carrier’s datacenter. This will enable developers to build optimized and scalable applications using Azure and directly connected to 5G networks, taking advantage of consistent Azure APIs and tooling available in the public cloud. We were the first public cloud to announce 5G integration with AT&T in Dallas in 2019, and now we're announcing a close collaboration with AT&T on a new Edge Zone targeted to become available in Los Angeles in late spring. Customers and partners interested in Edge Zones with AT&T can register for our early adopter program.

“This is a uniquely challenging time across the globe as we rethink how to help organizations serve their customers and stakeholders,” said Anne Chow, chief executive officer, AT&T Business. “Fast and intelligent mobile networks will be increasingly central to all of our lives. Combining our network knowledge and experience with Microsoft’s cloud expertise will give businesses a critical head start.”

These new zones will boost application performance, providing an optimal user experience when running ultra-low latency, sensitive mobile applications, and SIM-enabled architectures including:

  • Online gaming: Every press of the button, every click is important for a gamer. Responsiveness is critical, especially in multi-player scenarios. Game developers can now develop cloud-based applications optimized for mobile, directly accessing the 5G network at different carrier sites. They can achieve millisecond latency and scale to as many users as they want.
  • Remote meetings and events: As the prevalence of digital-forward experiences continue to rise in response to global health challenges, we can help bring together thousands of people to enjoy a real-time shared experience. Enabling scenarios like social engagement, mobile digital experiences, live interaction, and payment and processing require ultra-low latency to provide an immersive, responsive experience.
  • Smart Infrastructure: With the rise of IoT, organizations are looking to create efficiency, savings, and immersive experiences across residential and commercial buildings, or even citywide. With 5G and cloud computing, organizations can reliably connect millions of endpoints, analyze data, and deliver immersive experiences.

With Azure Edge Zones we’re expanding our collaboration with several of our carrier partners to bring the Azure Edge Zones family to our mutual customers later this year.

Listing of Azure Edge Zone partner ecosystem

In addition to partnering with carriers, we'll also deliver standalone Azure Edge Zones in select cities over the next 12 months, bringing Azure closer to customers and developers in highly dense areas.

Azure Private Edge Zones

We’re also announcing the preview of Azure Private Edge Zones, a private 5G/LTE network combined with Azure Stack Edge on-premises delivering an ultra-low latency, secure, and high bandwidth solution for organizations to enable scenarios, like with Attabotics, accelerating e-commerce delivery times by using 3D robotic goods-to-person storage, retrieval, and real-time order fulfillment solutions. This solution leverages Azure Edge Zones and IoT technologies such as Azure IoT Central and Azure Sphere.

 

“In collaboration with Microsoft, Rogers is delivering new and innovative solutions with our Private LTE capabilities combined with Azure Edge Zones,” said Dean Prevost, President, Rogers for Business. “Working with Attabotics, we’re enabling Canadian businesses to transform the traditional supply model with a retail e-fulfillment solution that showcases the exciting possibilities of today and opens the door to our 5G future.”

Partnering with the broad industry of carriers, systems integrators, and technology partners, we're launching a platform to support orchestration and management of customers' private cellular networks to enable scenarios such as:

  • Smart Factory/IoT: Off-shore operations or security isolated facilities can now take advantage of the power of edge computing. Connecting everything, from silicon to sensors, leveraging security to AI at the edge, deploying Digital Twins or using mixed reality, with a secure and private connection.
  • Logistics and operations: Retail customers have high expectations today in online and retail shopping, creating a need for appealing advertising before a potential customer looks away from a product on-line or in an aisle at the store. Wide selection, tailored offers, convenience, and availability are musts for success. The combination of cloud and distributed edge computing, efficiently working together is a game changer for the industry.
  • Medicine: From remote surgeries to complicated diagnostics that rely on cross-institutional collaboration, efficient compute and storage at the edge, with AI and minimal latency, enables these and multiple other scenarios that will save lives. Private mobile connections will work as smart grids for hospitals, patient data, and diagnostics that will never have to be exposed to the internet to take advantage of Azure technologies.

A consistent Edge Zone solution

Together, Azure, Azure Edge Zones, and Azure Private Edge Zones unlock a whole new range of distributed applications with a common and consistent architecture companies can use. For example, enterprises running a headquarters’ infrastructure on Azure, may leverage Azure Edge Zones for latency sensitive interactive customer experiences, and Azure Private Edge Zones for their remote locations. Enterprise solution providers can take advantage of the consistent developer, management, and security experience, allowing developers to continue using Github, Azure DevOps, and Kubernetes Services to create applications in Azure and simply move the application to either Azure Edge Zones or Private Edge Zones depending on the customer's requirements.

“By combining Vodafone 5G and mobile private networks with Azure Private Edge Zones, our customers will be able to run cloud applications on mobile devices with single-digit millisecond responsiveness. This is essential for autonomous vehicles and virtual reality services, for example, as these applications need to react in real-time to deliver business impact. It will allow organizations to innovate and transform their operations, such as the way their employees work with virtual reality services, high speed and precise robotics, and accurate computer vision for defect detection. Together, we expect Vodafone and Microsoft to provide our customers with the capabilities they need to create high performing, innovative and safe work environments.” - Vinod Kumar, CEO of Vodafone Business

Azure Private Edge Zones end-to-end partner and service ecosystem overview

New possibilities for the telecommunication industry with Azure

For the last few decades, carriers and operators have pioneered how we connect with each other, laying the foundation for telephony and cellular. With cloud and 5G, there are new possibilities by combining cloud services, including compute and AI, with mobile high bandwidth and ultra-low latency connections. Microsoft is partnering with carriers and operators to bring 5G to life in immersive applications built by organizations and developers.

Carriers, operators, and networking providers can build 5G-optimized services and applications for their partners and customers with Azure Edge Zones, taking advantage of Azure compute, storage, networking, and AI capabilities. For organizations that want an on-premises, private mobile solution, partners and carriers can deploy, manage, and build offers with Azure Private Edge Zones. Customers need help understanding the complexities of the cellular spectrum, access points, and overall management. Carrier partners can help such enterprises manage these scenarios including manufacturing, robotics, and retail.

In addition to new business application opportunities, we're looking to transform 5G infrastructure with cloud technology. Today, most 5G infrastructure is built on specialized hardware with high capital expenditures and little flexibility. Microsoft will be working to help operators reduce costs and build capacity for their network workloads in new and innovative ways. Last week, we announced the signing of a definitive agreement to acquire Affirmed Networks, a leader in fully virtualized cloud-native mobile network solutions. We look forward to building on their great work and technology expertise to do even more to create new opportunities for customers, technology partners, and operators in virtual mobile networks

As we continue to innovate and discover new, interesting ways to provide unique scenarios built with 5G and our Edge Zone platforms we will be sure to keep you updated. Please visit our page to learn more and keep track of the latest news here.


Guide to online meetings and live events

foreach 1.5.0 now available on CRAN

$
0
0

This post is to announce that version 1.5.0 of the foreach package is now on CRAN. Foreach is an idiom that allows for iterating over elements in a collection, without the use of an explicit loop counter. The foreach package is now more than 10 years old, and is used by nearly 700 packages across CRAN and Bioconductor. (If you're interested in an overview of the foreach package and its history, this RStudio::conf talk by Bryan Lewis is worth watching.)

The main change is 1.5.0 is intended to help reduce errors associated with modifying global variables. Now, %dopar% loops with a sequential backend will evaluate the loop body inside a local environment. Why make that change? Let's illustrate with an example:

library(foreach)
registerDoSEQ()

a <- 0
foreach(i=1:10) %dopar% {a <- a + 1}
a
## [1] 0

Here, the assignment inside the %dopar% is to a local copy of a, so the global variable a remains unchanged. The reason for this change is because %dopar% is intended for use in a parallel context, where modifying the global environment doesn’t make sense: the work will be taking place in different R processes, sometimes on different physical machines, possibly in the cloud. In this context there is no shared global environment to manipulate, unlike the case of a sequential backend.

Because of this, it’s almost always a mistake to modify global variables from %dopar%, even if it used to succeed. This change will hopefully reduce the incidence of programming errors where people prototype their code with a sequential backend, only to have it fail when they use it with a real (parallel) backend.

Note that the behaviour of the %do% operator, which is intended for a sequential backend, remains the same. It matches that of a regular for loop:

a <- 0
foreach(i=1:10) %do% {a <- a + 1}
a
## [1] 10

If you have any questions or comments, please email me or open an issue at the GitHub repo.

Plan for change: TLS 1.0 and TLS 1.1 soon to be disabled by default

$
0
0

As announced in October of 2018, Microsoft will soon disable Transport Layer Security (TLS) 1.0 and 1.1 by default in Microsoft browsers. In light of current global circumstances, we will be postponing this planned change—originally scheduled for the first half of 2020.

For the new Microsoft Edge (based on Chromium), TLS 1.0 and 1.1 are currently planned to be disabled by default no sooner than Microsoft Edge version 84 (currently planned for July 2020).

For all supported versions of Internet Explorer 11 and Microsoft Edge Legacy (EdgeHTML-based), TLS 1.0 and TLS 1.1 will be disabled by default as of September 8, 2020.

While these protocols will remain available for customers to re-enable as needed, we recommend that all organizations move off of TLS 1.0 and TLS 1.1 as soon as is practical. Newer versions of the TLS protocol enable more modern cryptography and are broadly supported across modern browsers, such as the new Microsoft Edge.

The post Plan for change: TLS 1.0 and TLS 1.1 soon to be disabled by default appeared first on Microsoft Edge Blog.

Improve virtual meetings and bring consultations online—here’s what’s new to Microsoft 365 in March

General availability of new Azure disk sizes and bursting

$
0
0

Today marks the general availability of new Azure disk sizes, including 4, 8, and 16 GiB on both Premium and Standard SSDs, as well as bursting support on Azure Premium SSD Disks.

To provide the best performance and cost balance for your production workloads, we are making significant improvements to our portfolio of Azure Premium SSD disks. With bursting, even the smallest Premium SSD disks (4 GiB) can now achieve up to 3,500 input/output operations per second (IOPS) and 170 MiB/second. If you have experienced jitters in disk IOs due to unpredictable load and spiky traffic patterns, migrate to Azure and improve your overall performance by taking advantage of bursting support.

We offer disk bursting on a credit-based system. You accumulate credits when traffic is below the provisioned target and you consume credit when traffic exceeds it. It can be best leveraged for OS disks to accelerate virtual machine (VM) boot or data disks to accommodate spiky traffic. For example, if you conduct a SQL checkpoint or your application issues IO flushes to persist the data, there will be a sudden increase of writes against the attached disk. Disk bursting will give you the headroom to accommodate the expected and unexpected change in load.

Disk bursting will be enabled by default for all new deployments of burst eligible disks with no user action required. For any existing Premium SSD Managed Disks (less than or equal to 512GiB/P20), whenever your disk is reattached or VM is restarted, disk bursting will start to take effect and your workloads can then experience a boost on disk performance. To read more about how disk bursting works, refer to this Premium SSD bursting article.

Further, the new disk sizes introduced on Standard SSD disk provide you the most cost-efficient SSD offering in the cloud, providing consistent disk performance at the lowest cost per GiB. We've also increased the performance target for all Standard SSD disks less than 64GiB (E6) to 500 IOPS. It is an ideal replacement of HDD based disk storage from either on-premises or cloud. It is best suited for hosting web servers, business applications that are not IO intensive but require stable and predictable performance for your business operations.

In this post, we’ll be sharing how you can start leveraging these new disk capabilities to build your most high performance, robust, and cost-efficient solution on Azure today.

Getting started

You can create new managed disks using the Azure portal, Powershell, or command-line interface (CLI) now. You can find the specifications of burst eligible and new disk sizes in the table below. Both new disk sizes and bursting support on Premium SSD Disks are available in all regions in Azure Public Cloud, with support for sovereign clouds coming soon.

Azure Premium SSD Managed Disks

Here are the burst eligible disks including the newly introduced sizes. Disk bursting doesn’t apply to disk sizes greater than 512 GiB (above P20) as the provisioned target of these sizes are sufficient for most workloads.  To learn more details on the disk sizes and performance targets, please refer to this "What disk types are available in Azure?" article.

30 mins

Burst capable disks Disk size Provisioned IOPS per disk Provisioned bandwidth per disk Max burst IOPS per disk Max burst bandwidth per disk Max burst duration at peak burst rate
P1—New 4 GiB 120 25 MiB/second 3,500 170 MiB/second 30 minutes
P2—New 8 GiB 120 25 MiB/second 3,500 170 MiB/second 30 minutes
P3—New 16 GiB 120 25 MiB/second 3,500 170 MiB/second

30 minutes

P4 32 GiB 120 25 MiB/second 3,500

170 MiB/second

30 minutes

P6 64 GiB 240 50 MiB/second 3,500

170 MiB/second

30 minutes
P10 128 GiB 500 100 MiB/second 3,500

170 MiB/second

30 minutes
P15 256 GiB 1,100 125 MiB/second 3,500 170/MiB/second 30 minutes
P20 512 GiB 2,300 150 MiB/second 3,500

170 MiB/second

30 minutes

Standard SSD Managed Disks

Here are the new disk sizes introduced on Standard SSD Disks. The performance targets define the max IOPS and bandwidth you can achieve on these sizes. Unlike Premium Disks, Standard SSD does not offer provisioned IOPS and bandwidth. For your performance-sensitive workloads or single instance deployment, we recommend leveraging Premium SSDs.    

  Disk size Max IOPS per disk Max bandwidth per disk
E1—New 4 GiB 500 25 MiB/second
E2—New 8 GiB 500 25 MiB/second
E3—New 16 GiB 500 25 MiB/second


Visit our service website to explore the Azure Disk Storage portfolio. To learn about pricing, you can visit the Azure Managed Disks pricing page

Your feedback

We look forward to hearing your feedback; please reach out to us here with your comments.

Announcing server-side encryption with customer-managed keys for Azure Managed Disks

$
0
0

Today, we're announcing the general availability for server-side encryption (SSE) with customer-managed keys (CMK) for Azure Managed Disks. Azure customers already benefit from SSE with platform-managed keys for Managed Disks enabled by default. SSE with CMK improves on platform-managed keys by giving you control of the encryption keys to meet your compliance need.

Today, customers can also use Azure Disk Encryption, which leverages the Windows BitLocker feature and the Linux dm-crypt feature to encrypt Managed Disks with CMK within the guest virtual machine (VM). SSE with CMK improves on Azure Disk encryption by enabling you to use any OS types and images, including custom images, for your VMs by encrypting data in the Azure Storage service.

SSE with CMK is integrated with Azure Key Vault, which provides highly available and scalable secure storage for your keys backed by Hardware Security Modules. You can either bring your own keys (BYOK) to your Key Vault or generate new keys in the Key Vault.

About the key management

Managed Disks are encrypted and decrypted transparently using 256-bit Advanced Encryption Standard (AES) encryption, one of the strongest block ciphers available. The Storage service handles the encryption and decryption in a fully transparent fashion using envelope encryption. It encrypts data using 256-bit AES-based data encryption keys, which are, in turn, protected using your keys stored in a Key Vault.

The Storage service generates data encryption keys and encrypts them with CMK using RSA encryption. The envelope encryption allows you to rotate (change) your keys periodically as per your compliance policies without impacting your VMs. When you rotate your keys, the Storage service re-encrypts the data encryption keys with the new CMK.

Full control of your keys

You are in full control of your keys in your Key Vault. Managed Disks uses system-assigned managed identity in your Azure Active Directory (Azure AD) for accessing keys in Key Vault. An administrator with required permissions in the Key Vault must first grant access to Managed Disks in Key Vault to use the keys for encrypting and decrypting the data encryption key. You can prevent Managed Disks from accessing your keys by either disabling your keys or by revoking access controls for your keys—doing so for disks attached to running VMs will cause the VMs to fail. Moreover, you can track the key usage through Key Vault monitoring to ensure that only Managed Disks or other trusted Azure services are accessing your keys.

Availability of SSE with CMK

SSE with CMK is available for Standard HDD, Standard SSD, and Premium SSD Managed Disks that can be attached to Azure Virtual Machines and VM scale sets. Ultra Disk Storage support will be announced separately. SSE with CMK is now enabled in all the public and Azure Government regions and will be available in the regions in Germany (Sovereign) and China in a few weeks.

You can use Azure Backup to back up your VMs using Managed Disks encrypted with SSE with CMK. Also, you can choose to encrypt the backup data in your Recovery Services vaults using your keys stored in your Key Vault instead of platform-managed keys available by default. Refer to documentation for more details on the encryption of backups using CMK.

You can use Azure Site Recovery to replicate your Azure virtual machines that have Managed Disks encrypted with SSE with CMK to other Azure regions for disaster recovery. You can also replicate your on-premises virtual machines to Managed Disks encrypted with SSE with CMK in Azure. Learn more about replicating your virtual machines using Managed Disks encrypted with SSE with CMK.

Get started

To enable the encryption with CMK for Managed Disks, you must first create an instance of a new resource type called DiskEncryptionSet and then grant the instance access to the key Vault. DiskEncryptionSet represents a key in your Key Vault and allows you to reuse the same key for encrypting many disks, snapshots, and images with the same key.

Let’s look at an example of creating an instance of DiskEncryptionSet:

1. Create an instance of DiskEncryptionSet by specifying a key in your Key Vault.

keyVaultId=$(az keyvault show --name yourKeyVaultName --query [id] -o tsv)

keyVaultKeyUrl=$(az keyvault key show --vault-name yourKeyVaultName --name yourKeyName --query [key.kid] -o tsv)

az disk-encryption-set create -n yourDiskEncryptionSetName -l WestCentralUS -g yourResourceGroupName --source-vault $keyVaultId --key-url $keyVaultKeyUrl

2. Grant the instance access to the Key Vault. When you created the instance, the system automatically created a system-assigned managed identity in your Azure AD and associated the identity with the instance. The identity must have access to the Key Vault to perform required operations such as wrapkey, unwrapkey and get.

desIdentity=$(az disk-encryption-set show -n yourDiskEncryptionSetName -g yourResourceGroupName --query [identity.principalId] -o tsv)

az keyvault set-policy -n yourKeyVaultName -g yourResourceGroupName --object-id $desIdentity --key-permissions wrapkey unwrapkey get

az role assignment create --assignee $desIdentity --role Reader --scope $keyVaultId

You are ready to enable the encryption for disks, snapshots, and images by associating them with the instance of DiskEncryptionSet. There is no restriction on the number of resources that can be associated with the same DiskEncryptionSet.

Let’s look at an example of enabling for an existing disk:

1. To enable the encryption for disks attached to a VM, you must stop(deallocate) a virtual machine.

az vm stop --resource-group MyResourceGroup --name MyVm

2. Enable the encryption for an attached disk by associating it with the instance of DiskEncryptionSet.

diskEncryptionSetId=$(az disk-encryption-set show -n yourDiskEncryptionSetName -g yourResourceGroupName --query [id] -o tsv)

az disk update -n yourDiskEncryptionSetName -g yourResourceGroupName --encryption-type EncryptionAtRestWithCustomerKey --disk-encryption-set $diskEncryptionSetId

3. Start the VM.

az vm start -g MyResourceGroup -n MyVm

Refer to the Managed Disks documentation for detailed instructions on enabling server side encryption with CMK for Managed Disks.

Send us your feedback

We look forward to hearing your feedback for SSE with CMK. Please email us here

New Azure RTOS collaborations with leaders in the semiconductor industry

$
0
0

IoT is reaching mainstream adoption across businesses in all market segments. Our vision is to enable Azure to be the world’s computer, giving businesses real-time visibility into every aspect of their operations, assets, and products. Businesses are harnessing signals from IoT devices of all shapes and sizes, from the very smallest microcontroller units (MCUs) to very capable microprocessor units (MPUs). This presents a great opportunity for collaboration between semiconductor manufacturers with extensive expertise in MCUs/MPUs and Azure IoT, an industry leader in IoT.

It has been nearly one year since we acquired Express Logic and their popular ThreadX RTOS, and last year we announced Azure RTOS that provides customers those capabilities with the leading real-time operating system (RTOS) in the industry.

Today, we’re announcing additional collaborations with industry leaders, which together represent the vast majority of the market for 32-bit MCUs. Their MCUs are embedded into billions of devices from sensors, streetlights, and shipping containers to smart home appliances, medical devices, and more.

STMicroelectronics, Renesas, NXP, Microchip, and Qualcomm will all offer embedded development kits featuring Azure RTOS ThreadX, one of the components of the Azure RTOS embedded application development suite. This allows embedded developers to access reliable, real-time performance for resource-constrained devices, and seamless integration with the power of Azure IoT to connect, monitor, and control a global fleet of IoT assets.

We will also be releasing the full source code for all Azure RTOS components on GitHub, allowing developers to freely explore, develop, test, and adapt Azure RTOS to suit their needs. When developers are ready to take their code into production, the production license will be included automatically if they deploy to any of the supported MCU devices from STMicroelectronics, Renesas, NXP, Microchip, or Qualcomm. If they prefer to use a different device in production, they may contact Microsoft for direct licensing details.

As we work with our semiconductor partners to implement best practices for connected devices, Azure RTOS will include easy-to-use reference projects and templates for connectivity to Azure IoT Hub, Azure IoT Central, Azure IoT Edge Gateways as well as first-class integration with Azure Security Center. Azure RTOS will soon ship with an Azure Security Center module for monitoring threats and vulnerabilities on IoT devices.

When combined with Azure Sphere, Azure RTOS enables embedded developers to quickly build real-time, highly-secured IoT devices for even the most demanding environments—robust devices that offer real-time performance and protection from evolving cybersecurity threats. For MCUs and system on chips (SoCs) that are smaller than what Azure Sphere supports, Azure RTOS and Azure IoT Hub Device Management enable secure communications for embedded developers and device operators who have the ability to implement best practices to protect devices from cybersecurity attacks.

For partners wishing to deliver reliable, real-time performance on highly-secured connected devices that stay secured against evolving cybersecurity threats over time, we recommend Azure RTOS and Azure Sphere together for the most demanding environments.

Here are more details on our collaboration with industry leaders.

STMicroelectronics (ST)

STMicroelectronics (ST) is a renowned world leader in ARM® Cortex®-M MCUs with its STM32 family, providing their OEM and mass-market customers with a wide portfolio of simple-to-use MCUs, coming with a complete development environment and best-in-class ecosystem.

“We are delighted to be collaborating with Microsoft to address even better our customers’ needs,” said Ricardo de Sa Earp, Group Vice-President, Microcontrollers Division General Manager, STMicroelectronics. “Leveraging our installed base of more than five billion STM32 MCUs shipped to date to the global embedded market, we see Azure RTOS ThreadX and middleware as a perfect match to both our mass-market and OEM IoT strategies, complementing our development environment with industry-proven, reliable, high-quality source code.” 

Renesas Electronics Corporation

Renesas Electronics Corporation is a premier supplier of advanced semiconductor solutions. Last October, we announced that Azure RTOS will be broadly available across Renesas' products, including the Synergy and RA MCU families. Renesas is also working to build Azure RTOS into their broader set of MCUs and MPUs.

“Our Synergy and RX cloud kits combined with Azure RTOS and other Azure IoT building blocks offer MCU customers a quick and secure end-to-end solution for cloud connectivity,” said Sailesh Chittipeddi, Executive Vice President, General Manager of Renesas’ IoT and Infrastructure business unit. “We are excited to expand our collaboration with Microsoft and look forward to bringing Microsoft Azure to our MCU and MPU customers, including solutions that will support Azure IoT Edge Runtime for Linux on our RZ MPUs.”

NXP Semiconductors 

NXP Semiconductors is a world leader in secure connectivity solutions for embedded applications, serving customers in the automotive, industrial and IoT, mobile, and communication infrastructure sectors. Microsoft has been collaborating with NXP to extend intelligent cloud computing to the intelligent edge, from adding voice control directly to devices to offering machine learning solutions for edge devices, to device security with Azure Sphere. They plan to integrate Azure RTOS into their evaluation kits and some of the most popular IoT processor families in the industry.

“Edge computing reduces the latency, bandwidth and privacy concerns of a cloud-only Internet of Things," said Jerome Schang, Head of Cloud Partnership programs at NXP. “Enabling Azure RTOS on NXP’s MCUs is yet another step to provide edge computing solutions that unlock the benefits of edge to Azure IoT cloud interaction.”

Microchip Technology, Inc.

Microchip Technology Inc. is a leading provider of smart, connected, and secure embedded control solutions. Their solutions serve customers across the industrial, automotive, consumer, aerospace and defense, communications, and computing markets. Microchip plans to incorporate support for Azure RTOS and Azure IoT Edge across their product families.

“Microchip is building on its already comprehensive portfolio of tools and solutions to enable quick, easy development of secure IoT applications across the full spectrum of embedded control devices and architectures,” said Greg Robinson, associate vice president of Microchip’s 8-bit microcontroller business unit. “Our partnership with Microsoft Azure extends our dedication to developing innovative solutions.”

Qualcomm Technologies, Inc.

Qualcomm is a pioneer of wireless technology and powers the cellular connection of smartphones and tablets all over the planet. Qualcomm will be offering a cellular-enabled Azure Sphere certified chip and will be bringing Azure RTOS to cellular-connected device solutions found inside asset trackers, health monitors, security systems, smart city sensors, and smart meters, as well as a range of wearables.

”Qualcomm is a leader in wireless compute and connectivity technologies – not just in mobile, but in emerging markets like the Internet of Things as well,” said Jeff Torrance, Vice President, IoT, Qualcomm. “We’re proud to continue to work closely with Microsoft on solutions like Azure RTOS and Azure Sphere to jointly advance the IoT industry around the world.”

Learn more

We continue to work diligently with industry-leaders to create a rich, robust ecosystem that serves the world’s unique and diverse needs. Our collective aim is to enable customers to easily bring their ideas to life and truly unlock the opportunities available on the intelligent edge and the intelligent cloud. Find out more about why so many IoT industry leaders are excited about the benefits that Azure RTOS brings to their device solutions.


Scrolling personality improvements in Microsoft Edge

$
0
0

Scrolling is one of the most common user interactions in a browser, and it’s central to how we experience the web.

Whether you’re using a touchpad, touch screen, mouse wheel, keyboard, or scroll bars, you want your scrolling experience to be fast and responsive.

In this post, we’ll cover how we’ve improved scrolling personality in Microsoft Edge – scroll animation and how it reacts to your interactions, looks and feel of scrolling.

Additionally, we’ll summarize themes we’ve observed in your feedback, and outline some of the next steps on our journey. In a future post we will describe some of the performance and functional improvements we’ve been working on.

Learning from the past

In previous releases of Microsoft Edge, we enabled smooth scrolling through tight integration with the operating system Compositor (DirectComposition) and input APIs (Direct Manipulation).  This allowed our browsers to introduce class leading smooth scrolling at the time and effortlessly match Windows personality – motion, interaction, looks and feel.

However, tight operating system integration meant that we couldn’t bring the experience to other OSes, including previous versions of Windows. Even worse, while processing input and output independently from the browsers main thread improved the responsiveness and allowed for a stable frame rate, it didn’t work great for script that performed updates based on frame updates, leading to jitter, one of the most common pieces of feedback we received at the time.

As time went on and features were added to the rendering pipeline, some features were incredibly hard to support in this model due to the dependency on the OS compositor – fixed position content with clipping ancestors, content with negative z-index, some z-index: auto scenarios, and CSS filters. In those cases, our users could experience missing or incorrectly clipped content, leading to non-interoperable experiences between browsers – broken sites.

With the new Microsoft Edge, we’re working to learn from our past experiences to improve scrolling for both Microsoft Edge and all Chromium-based browsers. One thing is clear from the outset: simply replicating the same Windows OS dependencies (with all its pros and cons) is not feasible, given the high bar for compatibility and cross-platform requirements for Microsoft Edge and other Chromium-based browsers.

Instead, together with the Chromium community, we are working to deliver meaningful user experience and performance improvements that will be more sustainable over time.

Before we dive into what’s new, let’s look at what you’ve told us about the scrolling so far.

Your scrolling feedback so far

Since releasing the first Canary builds of the new Microsoft Edge, we’ve received over 1000 feedback items ranging from positive encouragement to constructive feedback on various aspects of scrolling.

Analyzing all the issues allowed us to notice common themes in the feedback:

Chart showing top feedback themes by percentage

  • 41% of the feedback relates to what we call scroll “personality” – the way scrolling feels, and how it matches underlying operating system conventions and character, etc. This bucket can overlap with performance, which also impacts the “feel” of scrolling.
  • 39% of the feedback relates to functional issues – specific sites where scrolling doesn’t work as expected, or general issues with wheel, touch, touchpad, keyboard or scrollbar-based scrolling
  • 13% of the feedback relates to specific performance issues – missed frames while scrolling, scrolling stuttering, responsiveness issues etc.
  • 5% of the feedback relates to PDF scrolling – this could be further broken down to personality, functional and performance issues in PDF documents.
  • 2% of the feedback didn’t fall into any of the previous buckets

After considering your feedback, technical options, and taking into account our Chromium open source principles – we decided to focus our initial contributions most heavily on personality and performance.

In the remainder of this post, we’ll like to discuss the personality improvements we’ve made to Microsoft Edge to better match what you expect from other Windows apps including previous versions of Microsoft Edge.

Improving Chromium scrolling to better match Windows personality

We’ve been hard at work in the Chromium code base to bring the best aspects of Microsoft Edge scrolling to Chromium while looking for opportunities to improve upon it. These changes are now enabled by default in all channels of the new Microsoft Edge – try them out and let us know what you think!

Improved impulse and touch fling animation curves

One of the improvements we’re bringing to Chromium is a new animation curve for scrolling. This curve gives every mousewheel, keyboard or scrollbar scroll as well as touch fling the “smooth” personality seen in the previous version of Microsoft Edge.

Overall, the animation is more tactile with slightly longer with less abrupt changes in velocity. We encourage you to try it out today in the new Microsoft Edge on a Windows 10 device by scrolling using the mousewheel, keyboard or scrollbar or by using touch to do a fling.

Known issue: We’re refining the fling animation curve on some legacy non-PTP touchpads. Stay tuned for Insider announcements of further improvements in that area!

You can see some of the upstream changes in Chromium at the links below:

Percent-based scrolling

Chromium browsers use a fixed scroll delta value (100px per mousewheel tick, 40px per scrollbar button click or keyboard arrow press).  We are changing this behavior to match previous versions of Microsoft Edge, which use scroller height to compute scroll deltas. Percent based scrolling is a great functional addition making it much easier to navigate smaller scrollers.

You can see some of the upstream changes in Chromium at the links below:

Overscroll bounce effects on the root scroller

Overscroll bounce is a signal to the user that they’ve reached the end of a page while scrolling – you might have heard similar effects sometimes referred to as “rubber banding”. In user studies, 71% of participants expressed a preference for the overscroll bounce effect.

Animation showing the overscroll bounce effect

We’ve enabled this both for touch input and for PTP touchpad input in Microsoft Edge when scrolling in any direction.  Following initial support for the page’s root scroller, we’re investigating ways to enable this effect for sub-scrollers within the page in a future update – stay tuned!

Changes to previous Microsoft Edge personality based on the feedback

Scroll chaining vs. scroll latching

While we’ve brought much of the most-liked personality back to the new Microsoft Edge, we’re also using this opportunity to re-evaluate some existing behaviors our users were less fond of.

Consider scroll chaining, the effect that scrolls the parent scroller once the sub-scroller has reached its bounds. In the past we heard a substantial amount of feedback where many users considered this to be a bug on several popular sites.

Chromium already has a concept of scroll latching, when all scrolling manipulation is directed to the same scroller until a certain amount of time passes with no scroll changes.

The main difference between chaining and latching is that the former can occur mid-gesture if the sub-scroller bounds are reached, while the latter only works when the scroll gesture is started.

Animation showing scroll latching on a page

We’ve validated this change with user studies and noted that virtually everybody (97% favorable feedback) preferred this behavior if the overscroll bounce was also available – further validating our plans to invest in that effect for sub-scrollers.

As a result – we are not planning to bring scroll chaining to Chromium.

Fling boosting

Another thing we’re integrating with our animation curve that was already in Chromium is fling boosting. This is the experience where multiple sequential flings will continue to increase velocity. Fling boosting is great when you’re trying to quickly progress through the document and don’t care about precise content – only getting to your destination as quickly as possible.

Animation showing fling boosting on a page

We’re actively working on enabling fling boosting with the new scrolling personality and look forward to your feedback in the near future as we release this feature.

Quick Flick

Finally, we’ve decided to remove quick flick feature. This feature produced large scroll deltas when a user made a short but fast flick gesture on the screen.

In our user studies, we couldn’t find users who could reproduce it consistently. In addition to that, most feedback received for this feature was due to accidently performing a quick flick and the user not expecting the page to scroll so far.

Our scrolling personality backlog based on your feedback

We’re looking to make the new Microsoft Edge not just a carbon-copy of EdgeHTML, but an improvement that combines the best of Chromium with the best of EdgeHTML. To that end we’re evaluating other personality improvements and are investigating how to enable those in the coming updates, including:

  1. Overscroll effect in sub-scrollers
  2. Pinch zoom overscroll effect
  3. More tuning for individual personality effects and interactions based on your feedback

Please note – as we uncover engineering realities our plans might change!

Closing words

We hope you enjoyed reading this overview as much as we enjoyed working on improving scrolling based on your feedback!

Stay tuned for a follow-up post in this series, where we’ll discuss performance improvements and functional fixes – such as compositor-threaded scrollbar scrolling.

We’re looking for your feedback – please try the changes described in this post in Microsoft Edge and let us know what you think!

Bogdan Brinza, Principal Program Manager Lead
Clay Martin, Program Manager
– Sam Fortiner, Software Engineer

 

The post Scrolling personality improvements in Microsoft Edge appeared first on Microsoft Edge Blog.

Announcing .NET 5.0 Preview 2

$
0
0

Today, we’re releasing .NET 5.0 Preview 2. It contains a set of smaller features and performance improvements. We’re continuing to work on the bigger features that will define the 5.0 release, some of which are starting to show up as initial designs at dotnet/designs. The .NET 5.0 Preview 1 post covers what we are planning on building for .NET 5.0. Please take a look at the post and the designs repository and share any feedback you have. And, of course, please install Preview 2, and test any workloads you can with it.

You can download .NET 5.0 Preview 2, for Windows, macOS, and Linux:

ASP.NET Core and EF Core are also being released today.

You need to use Visual Studio 2019 16.6 to use .NET 5.0. Install the latest version of the C# extension, to use .NET 5.0 with Visual Studio Code. Visual Studio for Mac isn’t yet supported.

Release notes:

Let’s look at some of the improvements in Preview 2.

Code quality improvements in RyuJIT

Every release includes a set of changes that improve the machine code that the JIT generates (we call this “code quality”). Better code quality means better performance. In summary, about half of the following improvements are actual new optimizations and the other half are due to changing the flow of RyuJIT to enable existing optimizations to apply to more code patterns.

If you like this style of improvement or are an ARM64 user, you may be interested in Vectorise BitArray for ARM64, coming soon (already merged, but not in Preview 2). This change demonstrates a lot of focus on ARM64 in .NET 5.0.

Garbage Collector

Closing

Please take a moment to try out Preview 2, possibly in a container, a VM. We’d like your feedback on the quality of the release. There is a lot more coming, over the next several months, leading up a November release.

We’re running 50% of the .NET Website traffic on .NET 5.0 as a test case, using Azure load balancing. We’ve been doing that since days after we released Preview 1. You may remember us doing something similar with .NET Core 3.0 and 3.1. By splitting the traffic 50/50, we can ensure that 5.0 gets consistently better with constant performance data available to us. This model keeps us honest, and it’s also a great testing approach. We trust our most important site with ALL of our previews, in production (which we don’t recommend for anyone else). That should make adopting our final release a pretty easy choice for you. The version number is in the footer of .NET website; feel free to check at any time.

Stepping back for a minute, many of you might be interested in how the the folks on the .NET Team at Microsoft are doing. We’re doing well. We have lots of Teams meeting every day to organize and support one another, and are just as active on GitHub as ever. The team is close-knit, and collaborating across multiple time-zones. We’re all focused on this release, and very much intending to deliver what we set out to deliver when we first defined our plans, after releasing .NET Core 3.0. Take care.

The post Announcing .NET 5.0 Preview 2 appeared first on .NET Blog.

ASP.NET Core updates in .NET 5 Preview 2

$
0
0

.NET 5 Preview2 is now available and is ready for evaluation! .NET 5 will be a current release.

Get started

To get started with ASP.NET Core in .NET 5.0 Preview2 install the .NET 5.0 SDK.

If you’re on Windows using Visual Studio, we recommend installing the latest preview of Visual Studio 2019 16.6. If you’re on macOS, we recommend installing the latest preview of Visual Studio 2019 for Mac 8.6.

Upgrade an existing project

To upgrade an existing ASP.NET Core 5.0 preview1 app to ASP.NET Core 5.0 preview2:

  • Update all Microsoft.AspNetCore.* package references to 5.0.0-preview.2.20167.3.
  • Update all Microsoft.Extensions.* package references to 5.0.0-preview.2.20160.3.

See the full list of breaking changes in ASP.NET Core 5.0.

That’s it! You should now be all set to use .NET 5 Preview2.

What’s new?

ASP.NET Core in .NET 5 Preview 2 doesn’t include any major new features just yet, but it does include plenty of minor bug fixes. We expect to announce new features in upcoming preview releases.

See the release notes for additional details and known issues.

Give feedback

We hope you enjoy this release of ASP.NET Core in .NET 5! We are eager to hear about your experiences with this latest .NET 5 release. Let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core!

The post ASP.NET Core updates in .NET 5 Preview 2 appeared first on ASP.NET Blog.

Announcing Entity Framework Core 5.0 Preview 2

$
0
0

Announcing Entity Framework Core 5.0 Preview 2

Today we are excited to announce the second preview release of EF Core 5.0.

The second previews of .NET 5 and ASP.NET Core 5.0 are also available now.

Prerequisites

The previews of EF Core 5.0 require .NET Standard 2.1. This means:

  • EF Core 5.0 runs on .NET Core 3.1; it does not require .NET 5.
    • This may change in future previews depending on how the plan for .NET 5 evolves.
  • EF Core 5.0 runs on other platforms that support .NET Standard 2.1.
  • EF Core 5.0 will not run on .NET Standard 2.0 platforms, including .NET Framework.

How to get EF Core 5.0 previews

EF Core is distributed exclusively as a set of NuGet packages. For example, to add the SQL Server provider to your project, you can use the following command using the dotnet tool:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 5.0.0-preview.2.20159.4

The EF Core packages published today are:

We have also published the 5.0 preview 2 release of the Microsoft.Data.Sqlite.Core ADO.NET provider.

Installing dotnet ef

As with EF Core 3.0 and 3.1, the dotnet ef command-line tool is no longer included in the .NET Core SDK. Before you can execute EF Core migration or scaffolding commands, you’ll have to install this package as either a global or local tool.

To install the preview tool globally, first uninstall any existing version with:

dotnet tool uninstall --global dotnet-ef

Then install with:

dotnet tool install --global dotnet-ef --version 5.0.0-preview.2.20159.4
Image ef5preview2
EF 5 Preview 2

It’s possible to use this new version of dotnet ef with projects that use older versions of the EF Core runtime.


What’s new in EF Core 5 Preview 2

We maintain documentation covering new features introduced into each preview.

Some of the highlights from preview 2 are called out below.

Use a C# attribute to specify a property backing field

A C# attribute can now be used to specify the backing field for a property. This allows EF Core to still write to and read from the backing field as would normally happen, even when the backing field cannot be found automatically. For example:

public class Blog
{
    private string _mainTitle;

    public int Id { get; set; }

    [BackingField(nameof(_mainTitle))]
    public string Title
    {
        get => _mainTitle;
        set => _mainTitle = value;
    }
}

Documentation is tracked by issue #2230.

Complete discriminator mapping

EF Core uses a discriminator column for TPH mapping of an inheritance hierarchy. Some performance enhancements are possible so long as EF Core knows all possible values for the discriminator. EF Core 5.0 now implements these enhancements.

For example, previous versions of EF Core would always generate this SQL for a query returning all types in a hierarchy:

SELECT [a].[Id], [a].[Discriminator], [a].[Name]
FROM [Animal] AS [a]
WHERE [a].[Discriminator] IN (N'Animal', N'Cat', N'Dog', N'Human')

EF Core 5.0 will now generate the following when a complete discriminator mapping is configured:

SELECT [a].[Id], [a].[Discriminator], [a].[Name]
FROM [Animal] AS [a]

This will be the default behavior starting with preview 3.

Performance improvements in Microsoft.Data.Sqlite

We have made two performance improvements for SQLIte:

  • Retrieving binary and string data with GetBytes, GetChars, and GetTextReader is now more efficient by making use of SqliteBlob and streams.
  • Initialization of SqliteConnection is now lazy.

These improvements are in the ADO.NET Microsoft.Data.Sqlite provider and hence also improve performance outside of EF Core.


Daily builds

EF Core previews are aligned with .NET 5 previews. These previews tend to lag behind the latest work on EF Core. Consider using the daily builds instead to get the most up-to-date EF Core features and bug fixes.

As with the previews, the daily builds do not require .NET 5; they can be used with GA/RTM release of .NET Core 3.1.


Documentation and feedback

The starting point for all EF Core documentation is docs.microsoft.com/ef/core/.

Please file issues found and any other feedback on the dotnet/efcore GitHub repo.


Thank you from the team

A big thank you from the EF team to everyone who has used EF over the years!

ajcvickers
Arthur Vickers
AndriySvyryd
Andriy Svyryd

Brice Lambson
JeremyLikness
Jeremy Likness
lajones
lajones
maumar
Maurycy Markowski
roji
Shay Rojansky
smitpatel
Smit Patel

Thank you to our contributors!

A big thank you to the following community members who have already contributed code or documentation to the EF Core 5 release!

aevitas
aevitas
alaatm
Alaa Masoud
aleksandar-manukov
Aleksandar Manukov
amrbadawy
Amr Badawy
AnthonyMonterrosa
Anthony Monterrosa
bbrandt
Ben Brandt
benmccallum
Ben McCallum
ccjx
Clarence Cai
CGijbels
Christophe Gijbels
cincuranet
Jiri Cincura
Costo
Vincent Costel
dshuvaev
Dmitry Shuvaev
EricStG
Eric St-Georges
ErikEJ
Erik Ejlskov Jensen
gravbox
Christopher Davis
ivaylokenov
Ivaylo Kenov
jfoshee
Jacob Foshee
jmzagorski
Jeremy Zagorski
jviau
Jacob Viau
knom
Max K.
lohoris-crane
lohoris-crane
loic-sharma
Loïc Sharma
lokalmatador
lokalmatador
mariusGundersen
Marius Gundersen
Marusyk
Roman Marusyk
matthiaslischka
Matthias Lischka
MaxG117
MaxG117
MHDuke
MHDuke
mikes-gh
Mike Surcouf
Muppets
Neil Bostrom
nmichels
Nícolas Michels
OOberoi
Obi Oberoi
orionstudt
Josh Studt
ozantopal
Ozan Topal
pmiddleton
Paul Middleton
prog-rajkamal
Raj
ptjhuang
Peter Huang
ralmsdeveloper
Rafael Almeida Santos
redoz
Patrik Husfloen
rmarskell
Richard Marskell
sguitardude
sguitardude
SimpleSamples
Sam Hobbs
svengeance
Sven
VladDragnea
Vlad
vslee
vslee
WeihanLi
liweihan
Youssef1313
Youssef Victor
1iveowl
1iveowl
thomaslevesque
Thomas Levesque
akovac35
Aleksander Kovač
leotsarev
Leonid Tsarev
kostat
Konstantin Triger

The post Announcing Entity Framework Core 5.0 Preview 2 appeared first on .NET Blog.

Regex Performance Improvements in .NET 5

$
0
0

The System.Text.RegularExpressions namespace has been in .NET for years, all the way back to .NET Framework 1.1. It’s used in hundreds of places within the .NET implementation itself, and directly by thousands upon thousands of applications. Across all of that, it represents a significant source of CPU consumption.

However, from a performance perspective, Regex hasn’t received a lot of love in the intervening years. Its caching strategy was changed in .NET Framework 2.0 in the 2006 timeframe. .NET Core 2.0 saw the return of the implementation behind RegexOptions.Compiled (in .NET Core 1.x the RegexOptions.Compiled option was a nop). .NET Core 3.0 benefited from some of Regex‘s internals being updated to utilize Span<T> to improve memory utilization in certain scenarios. And along the way, a few very welcome community contributions have improved targeted areas, such as with dotnet/corefx#32899, which reduced accesses to CultureInfo.CurrentCulture when executing a RegexOptions.Compiled | RegexOptions.IgnoreCase expression. But beyond that, the implementation has largely been what it was 15 years ago.

For .NET 5 (Preview 2 of which was released this week), we’ve invested in some significant improvements to the Regex engine. On many of the expressions we’ve tried, these changes routinely result in throughput improvements of 3-6x, and in some cases, much more. In this post, I’ll walk through some of the myriad of changes that have gone into System.Text.RegularExpressions in .NET 5. The changes have had measurable impact on our own uses, and we hope these improvements will result in measurable wins in your libraries and apps, as well.

Regex Internals

To understand some of the changes made, it’s helpful to understand some Regex internals.

The Regex constructor does all the work to take the regular expression pattern and prepare to match inputs against it:

  • RegexParser. The pattern is fed into the internal RegexParser type, which understands the regular expression syntax and parses it into a node tree. For example, the expression a|bcd gets translated into an “alternation” RegexNode with two child nodes, one that represents the single character a and one that represents the “multi” bcd. The parser also does optimizations on the tree, translating one tree into another equivalent one that provides for a more efficient representation and/or that can be executed more efficiently.

  • RegexWriter. The node tree isn’t an ideal representation for performing a match, so the output of the parser is fed to the internal RegexWriter class, which writes out a compact series of opcodes that represent the instructions for performing a match. The name of this type is “writer” because it “writes” out the opcodes; other engines often refer to this as “compilation”, but the .NET engine uses different terminology because it reserves the “compilation” term for optional compilation to MSIL.

  • RegexCompiler (optional). If the RegexOptions.Compiled option isn’t specified, then the opcodes output by RegexWriter are used by the internal RegexInterpreter class later at match time to interpret/execute the instructions to perform the match, and nothing further is required during Regex construction. If, however, RegexOptions.Compiled is specified, then the constructor takes the previously output assets and feeds them into the internal RegexCompiler class. RegexCompiler then uses reflection emit to generate MSIL that represents the work the interpreter would do, but specialized for this specific expression. For example, when matching against the character ‘c’ in the pattern, the interpreter would need to load the comparison value from a variable, whereas the compiler hardcodes that ‘c’ as a constant in the generated IL.

Once the Regex has been constructed, it can be used for matching, via instance methods like IsMatch, Match, Matches, Replace, and Split (Match returns a Match object which then exposes a NextMatch method that enables iterating through the matches, lazily evaluated). Those operations end up in a “scan” loop (some other engines refer to it as a “transmission” loop) that at its core essentially does the following:

while (FindFirstChar())
{
    Go();
    if (_match != null)
        return _match;
    _pos++;
}
return null;

Here, _pos is the current position we’re at in the input. The virtual FindFirstChar starts at _pos and looks for the first place in the input text that the regular expression could possibly match; this is not executing the full engine, but rather doing as efficient as possible a search to find the location at which it would be worthwhile running the full engine. The better a job FindFirstChar can do at minimizing false positives and the faster it can find valid locations, the faster the expression will be processed. If it doesn’t find a good starting point, nothing will possibly match, so we’re done. If it does find a good starting point, it updates _pos and then it executes the engine at that found position, by invoking the virtual Go. If Go doesn’t find a match, we bump the current position and start over, but if Go does, it stores the match information and that data is returned. Obviously, the faster we can execute Go, too, the better.

All of this logic is in the public RegexRunner base class. RegexInterpreter derives from RegexRunner and overrides both FindFirstChar and Go with implementations that interpret the regular expression, as represented by the opcodes generated by RegexWriter. RegexCompiler uses DynamicMethods to generate two methods, one for FindFirstChar and one for Go; delegates are created from these and are invoked from another type derived from RegexRunner.

.NET 5 Improvements

For the rest of this post, we’ll walk through various optimizations that have been made for Regex in .NET 5. This is not an exhaustive list, but it highlights some of the most impactful changes.

CharInClass

Regular expressions support “character classes”, which define the set of characters that an input character should or should not match in order for that position to be considered a match. Character classes are delineated with square brackets. Here are some examples:

  • [abc]. Matches 'a', 'b', or 'c'.

  • [^n]. Matches anything other than a line feed character. (Unless RegexOptions.Singleline is specified, this is the exact character class you get from using . in an expression.)

  • [a-cx-z]. Matches 'a', 'b', 'c', 'x', 'y', or 'z'.

  • [dsp{IsGreek}]. Matches any Unicode digit or white-space or Greek character. (This is an interesting difference from most other regular expression engines. For example, in other engines, often d by default maps to [0-9] and you can opt-in to it instead mapping to all Unicode digits, which is [p{Nd}], whereas in .NET you get the latter by default and using RegexOptions.ECMAScript opts-out to the former.)

When a pattern containing a character class is passed to the Regex constructor, part of the RegexParser‘s job is to translate that character class into something it can more easily query at run-time. The parser uses the internal RegexCharClass type to parse the character class and extract essentially three things (there’s a bit more, but this is sufficient for this discussion):

  1. Whether the pattern is negated

  2. The sorted set of ranges of matching characters

  3. The sorted set of Unicode categories of matching characters

This is all implementation detail, but that information is then persisted in a string which can be passed to the protected RegexRunner.CharInClass method in order to determine whether a given Char is contained in the character class.

Transformed character class string

Prior to .NET 5, every single time a character needed to be matched against a character class, it would call that CharInClass method. CharInClass then does a binary search of the ranges to determine whether the specified character is stored in one, and if it’s not, it gets the Unicode category of the target character and does a linear search of the Unicode categories to see if it’s a match. So, for an expression like ^d*$ (which asserts that it’s at the start of the line, then matches any number of Unicode digits, and then asserts it’s at the end of the line), given an input of 1000 digits, this will make 1000 calls to CharInClass. That adds up.

In .NET 5, we’re now much smarter about how we do this, in particular when RegexOptions.Compiled is used, which in general is used any time a developer cares a lot about the throughput of a Regex. One solution would be, for every character class, to maintain a lookup table that maps an input character to a yes/no decision about whether it’s in the class or not. While we could do that, a System.Char is a 16-bit value, which means, at one bit per character, we’d need an 8K lookup table for every character class, and that also adds up. Instead, we first try to handle some common cases using existing functionality in the platform or otherwise simple math that makes matching fast. For example, for d, instead of generating a call to RegexRunner.CharInClass(ch, charClassString), we now simply generate a call to char.IsDigit(ch); IsDigit is already optimized with lookup tables, gets inlined, and performs very well. Similarly for s, we now generate a call to char.IsWhitespace(ch). For simple character classes that contain just a few characters, we’ll just generate the direct comparisons, e.g. for [az] we’ll generate the equivalent of (ch == 'a') | (ch == 'z'). And for simple character classes that contain just a single range, we’ll generate the check with a single subtraction and comparison, e.g. [a-z] results in (uint)ch - 'a' <= 26, and [^0-9] results in !((uint)c - '0' <= 10). We’ll also special-case other common specifications; for example, if the entire character class is a single Unicode category, we’ll just generate a call to char.GetUnicodeInfo (which also has a fast lookup table) and do the comparison, e.g. [p{Lu}] becomes char.GetUnicodeInfo(c) == UnicodeCategory.UppercaseLetter.

Of course, while that covers a large number of common cases, it certainly doesn’t cover them all. And just because we don’t want to generate an 8K lookup table for every character class doesn’t mean we can’t generate a lookup table at all. Instead, if we don’t hit one of these common cases, we do generate a lookup table, but just for ASCII, which only requires 16 bytes (128 bits), and which tends to be a very good compromise given typical inputs in regex-based scenarios. Since we’re generating methods using DynamicMethod, we don’t easily have the ability to store additional data in the static data portion of the assembly, but what we can do is utilize constant strings as a data store; MSIL has opcodes for loading constant strings, and reflection emit has good support for generating such instructions. So, for each lookup table, we can simply create the 8-character string we need, fill it with the opaque bitmap data, and spit that out with ldstr in the IL. We can then treat it as we would any other bitmap, e.g. to determine whether a given char ch matches, we generate the equivalent of this:

bool result = ch < 128 ? (lookup[c >> 4] & (1 << (c & 0xF))) != 0 : NonAsciiFallback;

In words, we use the top three bits of the character to select the 0th through 7th char in the lookup table string, and then use the bottom four bits as an index into the 16-bit value at that location; if it’s a 1, we matched, if it’s not, we didn’t. For characters then >= 128, we need a fallback, and that fallback can be a variety of things based on some analysis performed on the character class. Worst case, the fallback is just the call to RegexRunner.CharInClass we would have otherwise made, but we can often do better. For example, it’s common that we can tell from the input pattern that all possible matches will be < 128, in which case we don’t need a fallback at all, e.g. for the character class [0-9a-fA-F] (aka hexadecimal), we’ll instead generate the equivalent of:

bool result = ch < 128 && (lookup[c >> 4] & (1 << (c & 0xF))) != 0;

Conversely, we may be able to determine that every character above 127 is going to match. For example, the character class [^aeiou] (everything other than ASCII lowercase vowels) will instead result in code equivalent to:

bool result = ch >= 128 || (lookup[c >> 4] & (1 << (c & 0xF))) != 0;

And so on.

All of the above is for RegexOptions.Compiled, but interpreted expressions aren’t left out in the cold. For interpreted expressions, we currently generate a similar lookup table, but we do so lazily, populating the table for a given input character the first time we see it, and then storing that answer for all future evaluations of that character against that character class. (We might revisit how we do this, but this is how things exist as of .NET 5 Preview 2.)

The net result of this can be significant throughput gains on expressions that evaluate character classes frequently. For example, here’s a microbenchmark that’s matching ASCII letters and digits against an input with 62 such values:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Text.RegularExpressions;

public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);

    private Regex _regex = new Regex("[a-zA-Z0-9]*", RegexOptions.Compiled);
    
    [Benchmark] public bool IsMatch() => _regex.IsMatch("abcdefghijklmnopqrstuvwxyz123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}

And here’s my project file:

<project Sdk="Microsoft.NET.Sdk">
    <propertygroup>
        <langversion>preview</langversion>
        <outputtype>Exe</outputtype>
        <targetframeworks>netcoreapp5.0;netcoreapp3.1</targetframeworks>
    </propertygroup>
    
    <itemgroup>
        <packagereference Include="benchmarkdotnet" Version="0.12.0.1229"></packagereference>
    </itemgroup>
</project>

On my machine, I have two directories, one containing .NET Core 3.1 and one containing a build of .NET 5 (what’s labeled here as master, since it’s a build of the master branch from dotnet/runtime). When I execute the above with:

dotnet run -c Release -f netcoreapp3.1 --filter ** --corerun d:coreclrtestnetcore31corerun.exe d:coreclrtestmastercorerun.exe

to run the benchmark against both builds, I get these results:

Method Toolchain Mean Error StdDev Ratio
IsMatch mastercorerun.exe 102.3 ns 1.33 ns 1.24 ns 0.17
IsMatch netcore31corerun.exe 585.7 ns 2.80 ns 2.49 ns 1.00

Codegen Like a Dev Might Write

As mentioned, when RegexOptions.Compiled is used with a Regex, we use reflection emit to generate two methods for it, one to implement FindFirstChar and one to implement Go. To support the possibility of backtracking, Go ends up containing a lot of code that often isn’t necessary. The code is also generated in a way that often includes unnecessary field reads and writes, that incurs bounds checking the JIT is unable to eliminate, and so on. In .NET 5, we’ve improved the code generated for many expressions.

Consider the expression @"asb", which matches an 'a', any Unicode whitespace, and a 'b'. Previously, decompiling the IL emitted for Go would look something like this:

public override void Go()
{
    string runtext = base.runtext;
    int runtextstart = base.runtextstart;
    int runtextbeg = base.runtextbeg;
    int runtextend = base.runtextend;
    int num = runtextpos;
    int[] runtrack = base.runtrack;
    int runtrackpos = base.runtrackpos;
    int[] runstack = base.runstack;
    int runstackpos = base.runstackpos;

    CheckTimeout();
    runtrack[--runtrackpos] = num;
    runtrack[--runtrackpos] = 0;

    CheckTimeout();
    runstack[--runstackpos] = num;
    runtrack[--runtrackpos] = 1;

    CheckTimeout();
    if (num < runtextend && runtext[num++] == 'a')
    {
        CheckTimeout();
        if (num < runtextend && RegexRunner.CharInClass(runtext[num++], "u0001d"))
        {
            CheckTimeout();
            if (num < runtextend && runtext[num++] == 'b')
            {
                CheckTimeout();
                int num2 = runstack[runstackpos++];

                Capture(0, num2, num);
                runtrack[--runtrackpos] = num2;
                runtrack[--runtrackpos] = 2;
                goto IL_0131;
            }
        }
    }

    while (true)
    {
        base.runtrackpos = runtrackpos;
        base.runstackpos = runstackpos;
        EnsureStorage();
        runtrackpos = base.runtrackpos;
        runstackpos = base.runstackpos;
        runtrack = base.runtrack;
        runstack = base.runstack;

        switch (runtrack[runtrackpos++])
        {
            case 1:
                CheckTimeout();
                runstackpos++;
                continue;

            case 2:
                CheckTimeout();
                runstack[--runstackpos] = runtrack[runtrackpos++];
                Uncapture();
                continue;
        }

        break;
    }

    CheckTimeout();
    num = runtrack[runtrackpos++];
    goto IL_0131;

    IL_0131:
    CheckTimeout();
    runtextpos = num;
}

There’s a whole lot of stuff there, and it requires some squinting and searching to see the core of the implementation as just a few lines in the middle of the method. Now in .NET 5, that same expression results in this code being generated:

protected override void Go()
{
    string runtext = base.runtext;
    int runtextend = base.runtextend;
    int runtextpos;
    int start = runtextpos = base.runtextpos;
    ReadOnlySpan<char> readOnlySpan = runtext.AsSpan(runtextpos, runtextend - runtextpos);
    if (0u < (uint)readOnlySpan.Length && readOnlySpan[0] == 'a' &&
        1u < (uint)readOnlySpan.Length && char.IsWhiteSpace(readOnlySpan[1]) &&
        2u < (uint)readOnlySpan.Length && readOnlySpan[2] == 'b')
    {
        Capture(0, start, base.runtextpos = runtextpos + 3);
    }
}

If you’re anything like me, you look at the first version and your eyes glaze over, but if you look at the second, you can actually read and understand what it’s doing. Beyond being understandable and more easily debugged, it’s also less code to be executed, does better with bounds check eliminations, reads and writes fields and arrays less, and so on. The net result of this is it also executes much faster. (There’s some further possibility for improvements here, such as removing two length checks, potentially reordering some of the checks, but overall it’s a vast improvement over what was there before.)

Span-based Searching with Vectorized Methods

Regular expressions are all about searching for stuff. As a result, we often find ourselves running loops looking for various things. For example, consider the expression hello.*world. Previously if you were to decompile the code we generate in the Go method for matching the .*, it would look something like this:

while (--num3 > 0)
{
    if (runtext[num++] == 'n')
    {
        num--;
        break;
    }
}

In other words, we’re manually iterating through the input text string looking character by character for n (remember that by default a . means “anything other than n“, so .* means “match everything until you find n“). But, .NET has long had methods that do exactly such searches, like IndexOf, and as of recent releases, IndexOf is vectorized such that it can compare multiple characters at the same time rather than just looking at each individually. Now in .NET 5, instead of generating code like the above, we end up with code like this:

num2 = runtext.AsSpan(runtextpos, num).IndexOf('n');

Using IndexOf rather than generating our own loop then means that such searches in Regex are implicitly vectorized, and any improvements to such implementations also accrue here. It also means the generated code is simpler. The impact of this can be seen with a benchmark like this:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Text.RegularExpressions;

public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);

    private Regex _regex = new Regex("hello.*world", RegexOptions.Compiled);
    
    [Benchmark] public bool IsMatch() => _regex.IsMatch("hello.  this is a test to see if it's able to find something more quickly in the world.");
}

Even for an input string that’s not particularly large, this has a measurable impact:

Method Toolchain Mean Error StdDev Ratio
IsMatch mastercorerun.exe 71.03 ns 0.308 ns 0.257 ns 0.47
IsMatch netcore31corerun.exe 149.80 ns 0.913 ns 0.809 ns 1.00

IndexOfAny also ends up being a significant work-horse in .NET 5’s implementation, especially for FindFirstChar implementations. One of the existing optimizations the .NET Regex implementation employs is an analysis for what are all of the possible characters that could start an expression; that produces a character class, which FindFirstChar then uses to generate a search for the next location that could start a match. This can be seen by looking at a decompiled version of the generated code for the expression ([ab]cd|ef[g-i])jklm. A valid match to this expression can begin with only 'a', 'b', or 'e', and so the optimizer generates a character class [abe] which FindFirstChar then uses:

public override bool FindFirstChar()
{
    int num = runtextpos;
    string runtext = base.runtext;
    int num2 = runtextend - num;
    if (num2 > 0)
    {
        int result;
        while (true)
        {
            num2--;
            if (!RegexRunner.CharInClass(runtext[num++], "u0004acef"))
            {
                if (num2 <= 0)
                {
                    result = 0;
                    break;
                }
                continue;
            }
            num--;
            result = 1;
            break;
        }
        runtextpos = num;
        return (byte)result != 0;
    }
    return false;
}

A few things to note here:

  • We can see each character is evaluated via CharInClass, as discussed earlier. And we can see the string passed to CharInClass is the generated internal and searchable representation of that class (the first character says there’s no negation, the second character says there are four characters used to represent ranges, the third character says there are no Unicode categories, and then the next four characters represent two ranges, with an inclusive lower-bound and an exclusive upper-bound).

  • We can see that we evaluate each character individually, rather than being able to evaluate multiple characters together.

  • We only look at the first character, and if it’s a match, we exit out to allow the engine to execute in full for Go.

In .NET 5 Preview 2, we instead now generate this:

protected override bool FindFirstChar()
{
    int runtextpos = base.runtextpos;
    int runtextend = base.runtextend;
    if (runtextpos <= runtextend - 7)
    {
        ReadOnlySpan<char> readOnlySpan = runtext.AsSpan(runtextpos, runtextend - runtextpos);
        for (int num = 0; num < readOnlySpan.Length - 2; num++)
        {
            int num2 = readOnlySpan.Slice(num).IndexOfAny('a', 'b', 'e');
            num = num2 + num;
            if (num2 < 0 || readOnlySpan.Length - 2 <= num)
            {
                break;
            }

            int num3 = readOnlySpan[num + 1];
            if ((num3 == 'c') | (num3 == 'f'))
            {
                num3 = readOnlySpan[num + 2];
                if (num3 < 128 && ("ΐ"[num3 >> 4] & (1 << (num3 & 0xF))) != 0)
                {
                    base.runtextpos = runtextpos + num;
                    return true;
                }
            }
        }
    }

    base.runtextpos = runtextend;
    return false;
}

Some interesting things to note here:

  • We now use IndexOfAny to do the search for the three target characters. IndexOfAny is vectorized, so it can take advantage of SIMD instructions to compare multiple characters at once, and any future improvements we make to further optimize IndexOfAny will accrue to such FindFirstChar implementations implicitly.

  • If IndexOfAny finds a match, we don’t just immediately return in order to give Go a chance to execute. We instead do some fast checks on the next few characters to increase the likelihood that this is actually a match. In the original expression, you can see that the only possible values that could match the second character are 'c' and 'f', so the implementation does a fast comparison check for those. And you can see that the third character has to match either 'd' or [g-i], so the implementation combines those into a single character class [dg-i], which is then evaluate using a bitmap. Both of those latter character checks highlight the improved codegen that we now emit for character classes.

We can see the potential impact of this in a test like this:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Linq;
using System.Text.RegularExpressions;
    
public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);
    
    private static Random s_rand = new Random(42);
    
    private Regex _regex = new Regex("([ab]cd|ef[g-i])jklm", RegexOptions.Compiled);
    private string _input = string.Concat(Enumerable.Range(0, 1000).Select(_ => (char)('a' + s_rand.Next(26))));
    
    [Benchmark] public bool IsMatch() => _regex.IsMatch(_input);
}

which on my machine yields the results:

Method Toolchain Mean Error StdDev Ratio
IsMatch mastercorerun.exe 1.084 us 0.0068 us 0.0061 us 0.08
IsMatch netcore31corerun.exe 14.235 us 0.0620 us 0.0550 us 1.00

The previous code difference also highlights another interesting improvement, specifically the difference between the old code’s int num2 = runtextend - num; if (num2 > 0) and the new code’s if (runtextpos <= runtextend - 7). As mentioned earlier, the RegexParser parses the input pattern into a node tree, over which analysis and optimizations are performed. .NET 5 includes a variety of new analyses, some simple, some more complex. One of the more simpler examples is the parser will now do a quick scan of the expression to determine if there’s a minimum length that any input would have to be in order to match. Consider the expression [0-9]{3}-[0-9]{2}-[0-9]{4}, which might be used to match a United States social security number (three ASCII digits, a dash, two ASCII digits, a dash, four ASCII digits). We can easily see that any valid match for this pattern would require at least 11 characters; if we were provided with an input that was 10 or fewer, or if we got to within 10 characters of the end of the input without having found a match, we could immediately fail the match without proceeding further, because there’s no possible way it would match.

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Running;
using System.Text.RegularExpressions;

public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);

    private readonly Regex _regex = new Regex("[0-9]{3}-[0-9]{2}-[0-9]{4}", RegexOptions.Compiled);
    
    [Benchmark] public bool IsMatch() => _regex.IsMatch("123-45-678");
}
Method Toolchain Mean Error StdDev Ratio
IsMatch mastercorerun.exe 19.39 ns 0.148 ns 0.139 ns 0.04
IsMatch netcore31corerun.exe 459.86 ns 1.893 ns 1.771 ns 1.00

Backtracking Elimination

The .NET Regex implementation currently employs a backtracking engine. Such an implementation can support a variety of features that can’t easily or efficiently be supported by DFA-based engines, such as backreferences, and it can be very efficient in terms of memory utilization as well as in throughput for common cases. However, backtracking has a significant downside that it can lead to degenerate cases where matching takes exponential time in the length of the input. This is why the .NET Regex class exposes the ability to set a timeout, so that runaway matching can be interrupted by an exception.

The .NET documentation has more details, but suffice it to say, it’s up to the developer to write regular expressions in a way that are not subject to excessive backtracking. One of the ways to do this is to employ “atomic groups”, which tell the engine that once the group has matched, the implementation must not backtrack into it, and it’s generally used when such backtracking won’t be beneficial. Consider an example expression a+b matched against the input aaaa:

  • The Go engine starts matching a+. This operation is greedy, so it matches the first a, then the aa, then the aaa, and then the aaaa. It then sees it’s at the end of the input.

  • There’s no b to match, so the engine backtracks 1, with the a+ now matching aaa.

  • There’s still no b to match, so the engine backtracks 1, with the a+ now matching aa.

  • There’s still no b to match, so the engine backtracks 1, with the a+ now matching a.

  • There’s still no b to match, and a+ requires at least 1 a, so the match fails.

But, all of that backtracking was provably unnecessary. There’s nothing the a+ could match that the b could have also matched, so no amount of backtracking here would be fruitful. Seeing that, the developer could instead use the expression (?>a+)b. That (?> and ) are the start and end of an atomic group, which says that once that group has matched and the engine moves past the group, it must not backtrack back into it. With our previous example of matching against aaaa then, this would happen instead:

  • The Go engine starts matching a+. This operation is greedy, so it matches the first a, then the aa, then the aaa, and then the aaaa. It then sees it’s at the end of the input.

  • There’s no b to match, so the match fails.

Much shorter, and this is just a simple example. So, a developer can do this analysis themselves and find places to manually insert atomic groups, but really, how many developers think to do that or take the time to do so?

Instead, .NET 5 now analyzes regular expressions as part of the node tree optimization phase, adding atomic groups where it sees that they won’t make a semantic difference but could help to avoid backtracking. For example:

  • a+b will become (?>a+)b, as there’s nothing a+ can “give back” that’ll match b

  • d+s* will become (?>d+)(?>s*), as there’s nothing that could match d that could also match s, and s is at the end of the expression.

  • a*([xyz]|hello) will become (?>a*)([xyz]|hello), as in a successful match a could be followed by x, by y, by z, or by h, and there’s no overlap with any of those.

This is just one example of tree rewriting that .NET 5 will now perform. It’ll do other rewrites, in part with a goal of eliminating backtracking. For example, it’ll now coalesce various forms of loops that are adjacent to each other. Consider the degenerate example a*a*a*a*a*a*a*b. In .NET 5, this will now be rewritten to just be the functionally equivalent a*b, which per the previous discussion will then further be rewritten as (?>a*)b. That turns a potentially very expensive execution into one with linear execution time. It’s almost pointless showing an example benchmark, as we’re dealing with different algorithmic complexities, but I’ll do it anyway, just for fun:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Text.RegularExpressions;
    
public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);
    
    private Regex _regex = new Regex("a*a*a*a*a*a*a*b", RegexOptions.Compiled);
    
    [Benchmark] public bool IsMatch() => _regex.IsMatch("aaaaaaaaaaaaaaaaaaaaa");
}
Method Toolchain Mean Error StdDev Ratio
IsMatch mastercorerun.exe 379.2 ns 2.52 ns 2.36 ns 0.000
IsMatch netcore31corerun.exe 22,367,426.9 ns 123,981.09 ns 115,971.99 ns 1.000

Backtracking reduction isn’t limited just to loops. Alternations represent another source of backtracking, as the implementation will match in a manner similar to how you might if you were matching by hand: try one alternation branch and proceed as far as you can, and then if the match fails, go back and try the next branch, and so on. Thus, reduction of backtracking from alternations is also useful.

One such rewrite now performed has to do with alternation prefix factoring. Consider the expression what is (?:this|that) evaluated against the text what is it. The engine will match the what is, and will then try to match it against this; it won’t match, so it’ll backtrack and try to match it against that. But both branches of the alternation begin with th. If we instead factor that out, and rewrite the expression to be what is th(?:is|at), we can now avoid the backtracking. The engine will match the what is, will try to match the th against the it, will fail, and that’s it.

This optimization also ends up exposing more text to an existing optimization used by FindFirstChar. If there are multiple fixed characters at the beginning of the pattern, FindFirstChar will use a Boyer-Moore implementation to find that text in the input string. The larger the pattern exposed to the Boyer-Moore algorithm, the better it can do at quickly finding a match and minimizing false positives that would cause FindFirstChar to exit to the Go engine. By pulling text out of such an alternation, we increase the amount of text available in this case to Boyer-Moore.

As another related example, .NET 5 now finds cases where the expression can be implicitly anchored even if the developer didn’t specify to do so, which can also help with backtracking elimination. Consider the example .*hello with the input abcdefghijk. The implementation will start at position 0 and evaluate the expression at that point. Doing so will match the whole string abcdefghijk against the .*, and will then backtrack from there to try to match the hello, which it will fail to do. The engine will fail the match, and we’ll then bump to the next position. The engine will then match the rest of the string bcdefghijk against the .*, and will then backtrack from there to try to match the hello, which it will again fail to do. And so on. The observation to make here is that such retries by bumping to the next position are generally not going to be successful, and the expression can be implicitly anchored to only match at the beginning of lines. That then enables FindFirstChar to skip positions that can’t possibly match and avoid the attempted engine match at those locations.

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Running;
using System.Text.RegularExpressions;

public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);

    private readonly Regex _regex = new Regex(@".*text", RegexOptions.Compiled);
    
    [Benchmark] public bool IsMatch() => _regex.IsMatch("This is a test.nDoes it match this?nWhat about this text?");
}
Method Toolchain Mean Error StdDev Ratio
IsMatch mastercorerun.exe 644.1 ns 3.63 ns 3.39 ns 0.21
IsMatch netcore31corerun.exe 3,024.9 ns 22.66 ns 20.09 ns 1.00

(Just to make sure it’s clear, many regular expressions will still employ backtracking in .NET 5, and thus developers still need to be careful about running untrusted regular expressions.)

Regex.* static methods and concurrency

The Regex class exposes both instance and static methods. The static methods are primarily intended as a convenience, as under the covers they still need to use and operate on a Regex instance. The implementation could instantiate a new Regex and go through the full parsing/optimization/codegen routine each time one of these static methods was used, but in some scenarios that would be an egregious waste of time and space. Instead, Regex maintains a cache of the last few Regex objects used, indexed by everything that makes them unique, e.g. the pattern, the RegexOptions, even the current culture (since that can impact IgnoreCase matching). This cache is limited in size, capped at Regex.CacheSize, and thus the implementation employs a least recently used (LRU) cache: when the cache is full and another Regex needs to be added, the implementation throws away the least recently used item in the cache.

One simple way to implement such an LRU cache is with a linked list: every time an item is accessed, it’s removed from the list and added back to the front. Such an approach, however, has a big downside, in particular in a concurrent world: synchronization. If every read is actually a mutation, we need to ensure that concurrent reads, and thus concurrent mutations, don’t corrupt the list. Such a list is exactly what previous releases of .NET employed, and a global lock was used to protect it. In .NET Core 2.1, a nice change submitted by a community member improved this for some scenarios by making it possible to access the most recently used item lock-free, which improved throughput and scalability for workloads where the same Regex was used via the static methods repeatedly. For other cases, however, the implementation still locked on every usage.

It’s possible to see the impact of this locking by looking at a tool like the Concurrency Visualizer, which is an extension to Visual Studio available in its extensions gallery. By running a sample app like this under the profiler:

using System.Text.RegularExpressions;
using System.Threading.Tasks;
    
class Program
{
    static void Main()
    {
        Parallel.Invoke(
            () => { while (true) Regex.IsMatch("abc", "^abc$"); },
            () => { while (true) Regex.IsMatch("def", "^def$"); },
            () => { while (true) Regex.IsMatch("ghi", "^ghi$"); },
            () => { while (true) Regex.IsMatch("jkl", "^jkl$"); });
    }
}

we can see images like this:

Contention from Regex static methods

Each row is a thread doing work as part of this Parallel.Invoke. Areas in green are when the thread is actually executing code. Areas in yellow are when the thread has been pre-empted by the operating system because it needed the core to run another thread. Areas in red are when the thread is blocked waiting for something. In this case, all that red is because threads are waiting on that shared global lock in the Regex cache.

With .NET 5, the picture instead looks like this:

No contention from Regex static methods

Notice, no more red. This is because the cache has been re-written to be entirely lock-free for reads; the only time a lock is taken is when a new Regex is added to the cache, but even while that’s happening, other threads can proceed to read instances from the cache and use them. This means that as long as Regex.CacheSize is properly sized for an app and its typical usage of the Regex static methods, such accesses will no longer incur the kinds of delays they did in the past. As of today, the value defaults to 15, but the property has a setter so that it can be changed to better suit an app’s needs.

Allocation for the static methods has also been improved, by changing exactly what is cached so as to avoid allocating unnecessary wrapper objects. We can see this with a modified version of the previous example:

using System.Text.RegularExpressions;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Parallel.Invoke(
            () => { for (int i = 0; i < 10_000; i++) Regex.IsMatch("abc", "^abc$"); },
            () => { for (int i = 0; i < 10_000; i++) Regex.IsMatch("def", "^def$"); },
            () => { for (int i = 0; i < 10_000; i++) Regex.IsMatch("ghi", "^ghi$"); },
            () => { for (int i = 0; i < 10_000; i++) Regex.IsMatch("jkl", "^jkl$"); });
    }
}

and running it with the .NET Object Allocation Tracking tool in Visual Studio. On the left is .NET Core 3.1, and on the right is .NET 5 Preview 2:

Reduced allocation from Regex static methods

In particular, note the line containing 40,000 allocations on the left that’s instead only 4 on the right.

Other Overhead Reduction

We’ve walked through some of the key improvements that have gone into regular expressions in .NET 5, but the list is by no means complete. There is a laundry list of smaller optimizations that have been made all over the place, and while we can’t enumerate them all here, we can walk through a few more.

In some places, we’ve utilized forms of vectorization beyond what was discussed previously. For example, when RegexOptions.Compiled is used and the pattern contains a string of characters, the compiler emits a check for each character individually. You can see this if you look at the decompiled code for an expression like abcd, which would previously result in code something like this:

if (4 <= runtextend - runtextpos &&
    runtext[runtextpos] == 'a' &&
    runtext[runtextpos + 1] == 'b' &&
    runtext[runtextpos + 2] == 'c' &&
    runtext[runtextpos + 3] == 'd')

In .NET 5, when using DynamicMethod to create the compiled code, we now instead try to compare Int64 values (on a 64-bit system, or Int32s on a 32-bit system), rather than comparing individual Chars. That means for the previous example we’ll instead now generate code akin to this:

if (3u < (uint)readOnlySpan.Length && *(long*)readOnlySpan._pointer == 28147922879250529L)

(I say “akin to”, because we can’t represent in C# the exact IL that’s generated, which is more aligned with using members of the Unsafe type.). We don’t need to worry about endianness issues here, because the code generating the Int64/Int32 values used for comparison is happening on the same machine (and even in the same process) as the one loading the input values for comparison.

Another example is something that was actually shown earlier in a previous generated code sample, but glossed over. You may have noticed earlier on when comparing the outputs for the @"asb" expression that the previous code contained calls to CheckTimeout() but the new code did not. This CheckTimeout() function is used to check whether our execution time has exceeded what’s allowed by the timeout value provided to the Regex when it was constructed. But the default timeout used when no timeout is provided is “infinite”, and thus “infinite” is a very common value. Since we will never exceed an infinite timeout, when we compile the code for a RegexOptions.Compiled regular expression, we check the timeout, and if it’s infinite, we skip generating these CheckTimeout() calls.

Similar optimizations exist in other places. For example, by default Regex does case-sensitive comparisons. Only if RegexOptions.IgnoreCase is specified (or if the expression itself contains instructions to perform case-insensitive matches) are case-insensitive comparisons used, and only if case-insensitive comparisons are used do we need to access CultureInfo.CurrentCulture in order to determine how to perform that comparison. Further, if RegexOptions.InvariantCulture is specified, we also don’t need to access CultureInfo.CurrentCulture, because it’ll never be used. All of this means we can avoid generating the code that accesses CultureInfo.CurrentCulture if we prove that it’ll never be needed. On top of that, we can make RegexOptions.InvariantCulture faster by emitting calls to char.ToLowerInvariant rather than char.ToLower(CultureInfo.InvariantCulture), especially since ToLowerInvariant has also been improved in .NET 5 (yet another example where by changing Regex to use other framework functionality, it implicitly benefits any time we improve those utilized functions).

Another interesting change was to Regex.Replace and Regex.Split. These methods were implemented as wrappers around Regex.Match, layering their functionality on top of it. That, however, meant that every time a match was found, we would exit the scan loop, work our way back up through the various layers of abstraction, perform the work on the match, and then call back into the engine, work our way back down to the scan loop, and so on. On top of that, each match would require a new Match object be created. Now in .NET 5, there’s a dedicated callback-based loop used internally by these methods, which lets us stay in the tight scan loop and reuse the same Match object over and over (something that wouldn’t be safe if exposed publicly but which can be done as an internal implementation detail). The memory management used in implementing Replace has also been tuned to focus on tracking regions of the input to be replaced or not, rather than tracking each individual character. The net effect of this can be fairly impactful on both throughput and memory allocation, in particular for very long inputs with relatively few replacements.

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Linq;
using System.Text.RegularExpressions;
    
[MemoryDiagnoser]
public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);
    
    private Regex _regex = new Regex("a", RegexOptions.Compiled);
    private string _input = string.Concat(Enumerable.Repeat("abcdefghijklmnopqrstuvwxyz", 1_000_000));
    
    [Benchmark] public string Replace() => _regex.Replace(_input, "A");
}
Method Toolchain Mean Error StdDev Ratio Gen 0 Gen 1 Gen 2 Allocated
Replace mastercorerun.exe 93.79 ms 1.120 ms 0.935 ms 0.45 81.59 MB
Replace netcore31corerun.exe 209.59 ms 3.654 ms 3.418 ms 1.00 33666.6667 666.6667 666.6667 371.96 MB

“Show Me The Money”

All of this comes together to produce significantly better performance on a wide array of benchmarks. To exemplify that, I searched around the web for regex benchmarks and ran several.

The benchmarks at mariomka/regex-benchmark already had a C# version, so that was an easy thing to simply compile and run:

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;

class Benchmark
{
    static void Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.WriteLine("Usage: benchmark <filename>");
            Environment.Exit(1);
        }

        StreamReader reader = new System.IO.StreamReader(args[0]);
        string data = reader.ReadToEnd();
    
        // Email
        Benchmark.Measure(data, @"[w.+-]+@[w.-]+.[w.-]+");
    
        // URI
        Benchmark.Measure(data, @"[w]+://[^/s?#]+[^s?#]+(?:?[^s#]*)?(?:#[^s]*)?");
    
        // IP
        Benchmark.Measure(data, @"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9])");
    }
    
    static void Measure(string data, string pattern)
    {
        Stopwatch stopwatch = Stopwatch.StartNew();
    
        MatchCollection matches = Regex.Matches(data, pattern, RegexOptions.Compiled);
        int count = matches.Count;
    
        stopwatch.Stop();
    
        Console.WriteLine(stopwatch.Elapsed.TotalMilliseconds.ToString("G", System.Globalization.CultureInfo.InvariantCulture) + " - " + count);
    }
}

On my machine, here’s the console output using .NET Core 3.1:

966.9274 - 92
746.3963 - 5301
65.6778 - 5

and the console output using .NET 5:

274.3515 - 92
159.3629 - 5301
15.6075 - 5

The numbers before the dashes are the execution times, and the numbers after the dashes are the answers (so it’s a good thing that the second numbers remain the same). The execution times drop precipitously: that’s a 3.5x, 4.6x, and 4.2x improvement, respectively!

I also found https://zherczeg.github.io/sljit/regex_perf.html, which has a variety of benchmarks but no C# version. I translated it into a Benchmark.NET test:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.IO;
using System.Text.RegularExpressions;
    
[MemoryDiagnoser]
public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args);
    
    private static string s_input = File.ReadAllText(@"d:mtent12.txt");
    private Regex _regex;
    
    [GlobalSetup]
    public void Setup() => _regex = new Regex(Pattern, RegexOptions.Compiled);
    
    [Params(
        @"Twain",
        @"(?i)Twain",
        @"[a-z]shing",
        @"Huck[a-zA-Z]+|Saw[a-zA-Z]+",
        @"bw+nnb",
        @"[a-q][^u-z]{13}x",
        @"Tom|Sawyer|Huckleberry|Finn",
        @"(?i)Tom|Sawyer|Huckleberry|Finn",
        @".{0,2}(Tom|Sawyer|Huckleberry|Finn)",
        @".{2,4}(Tom|Sawyer|Huckleberry|Finn)",
        @"Tom.{10,25}river|river.{10,25}Tom",
        @"[a-zA-Z]+ing",
        @"s[a-zA-Z]{0,12}ings",
        @"([A-Za-z]awyer|[A-Za-z]inn)s"
    )]
    public string Pattern { get; set; }
    
    [Benchmark] public bool IsMatch() => _regex.IsMatch(s_input);
}

and ran it against the ~20MB text file input provided from that page, getting the following results:

Method Toolchain Pattern Mean Ratio
IsMatch mastercorerun.exe (?i)T(…)Finn [31] 12,703.08 ns 0.32
IsMatch netcore31corerun.exe (?i)T(…)Finn [31] 40,207.12 ns 1.00
IsMatch mastercorerun.exe (?i)Twain 159.81 ns 0.84
IsMatch netcore31corerun.exe (?i)Twain 189.49 ns 1.00
IsMatch mastercorerun.exe ([A-Z(…)nn)s [29] 6,903,345.70 ns 0.10
IsMatch netcore31corerun.exe ([A-Z(…)nn)s [29] 67,388,775.83 ns 1.00
IsMatch mastercorerun.exe .{0,2(…)Finn) [35] 1,311,160.79 ns 0.68
IsMatch netcore31corerun.exe .{0,2(…)Finn) [35] 1,942,021.93 ns 1.00
IsMatch mastercorerun.exe .{2,4(…)Finn) [35] 1,202,730.97 ns 0.67
IsMatch netcore31corerun.exe .{2,4(…)Finn) [35] 1,790,485.74 ns 1.00
IsMatch mastercorerun.exe Huck[(…)A-Z]+ [26] 282,030.24 ns 0.01
IsMatch netcore31corerun.exe Huck[(…)A-Z]+ [26] 19,908,290.62 ns 1.00
IsMatch mastercorerun.exe Tom.{(…)5}Tom [33] 8,817,983.04 ns 0.09
IsMatch netcore31corerun.exe Tom.{(…)5}Tom [33] 94,075,640.48 ns 1.00
IsMatch mastercorerun.exe TomS(…)Finn [27] 39,214.62 ns 0.14
IsMatch netcore31corerun.exe TomS(…)Finn [27] 281,452.38 ns 1.00
IsMatch mastercorerun.exe Twain 64.44 ns 0.77
IsMatch netcore31corerun.exe Twain 83.61 ns 1.00
IsMatch mastercorerun.exe [a-q][^u-z]{13}x 1,695.15 ns 0.09
IsMatch netcore31corerun.exe [a-q][^u-z]{13}x 19,412.31 ns 1.00
IsMatch mastercorerun.exe [a-zA-Z]+ing 3,042.12 ns 0.31
IsMatch netcore31corerun.exe [a-zA-Z]+ing 9,896.25 ns 1.00
IsMatch mastercorerun.exe [a-z]shing 28,212.30 ns 0.24
IsMatch netcore31corerun.exe [a-z]shing 117,954.06 ns 1.00
IsMatch mastercorerun.exe bw+nnb 32,278,974.55 ns 0.21
IsMatch netcore31corerun.exe bw+nnb 152,395,335.00 ns 1.00
IsMatch mastercorerun.exe s[a-(…)ings [21] 1,181.86 ns 0.23
IsMatch netcore31corerun.exe s[a-(…)ings [21] 5,161.79 ns 1.00

Some of those ratios are quite lovely.

Another is the “regex-redux” benchmark from the “The Computer Language Benchmarks Game”. There’s an implementation of this harnessed in the dotnet/performance repo, so I ran that:

Method Toolchain options Mean Error StdDev Median Min Max Ratio RatioSD Gen 0 Gen 1 Gen 2 Allocated
RegexRedux_5 mastercorerun.exe Compiled 7.941 ms 0.0661 ms 0.0619 ms 7.965 ms 7.782 ms 8.009 ms 0.30 0.01 2.67 MB
RegexRedux_5 netcore31corerun.exe Compiled 26.311 ms 0.5058 ms 0.4731 ms 26.368 ms 25.310 ms 27.198 ms 1.00 0.00 1571.4286 12.19 MB

Thus on this benchmark, .NET 5 is 3.3x the throughput of .NET Core 3.1.

Call to Action

We’d love your feedback and contributions in multiple ways.

Download .NET 5 Preview 2 and try it out with your regular expressions. Do you see measurable gains? If so, tell us about it. If not, tell us about that, too, so that we can work together to find ways to improve things for your most valuable expressions.

Are there specific regular expressions that are important to you? If so, please share them with us; we’d love to augment our test suite with real-world regular expressions from you, your input data, and the corresponding expected results, so as to help ensure that we don’t regress things important to you as we make further improvements to the code base. In fact, we’d welcome PRs to dotnet/runtime to augment the test suite in just that way. You can see that in addition to thousands of synthetic test cases, the Regex test suite contains a bunch of examples sourced from documentation, tutorials, and real applications; if you have expressions you think should be added here, please submit PRs. We’ve changed a lot of code as part of these performance improvements, and while we’ve been diligent about validation, surely some bugs have crept in. Feedback from you with your important expressions will help to shore this up!

As much work as has been done in .NET 5 already, we also have a laundry list of additional known work that can be explored, catalogued at dotnet/runtime#1349. We would welcome additional suggestions here, and more so actual prototyping or productizing of some of the ideas outlined there (with appropriate performance vetting, testing, etc.) Some examples:

  • Improve the automatic addition of atomic groups for loops. As noted in this post, we now automatically insert atomic groups in a bunch of places where we can detect they may help reduce backtracking while keeping semantics identical. We know, however, there are some gaps in our analysis, and it’d be great to fill those. For example, the implementation will now change a*b+c to be (?>a*)(?>b+)c, as it will see that b+ won’t give anything back that can match c, and a* won’t give anything back that can match b (and the b+ means there must be at least one b). However, the expression a*b*c will be transformed into a*(?>b*)c rather than (?>a*)(?>b*)c, even though the latter is appropriate. The issue here is we only currently look at the next node in the sequence, and here the b* may match zero items which means the next node after the a* could be the c, and we don’t currently look that far ahead.

  • Improve the automatic addition of atomic groups for alternations. We can do more to automatically upgrade alternations to be atomic based on an analysis of the alternation. For example, given an expression like (Bonjour|Hello), .*, we know that if Bonjour matched, there’s no possible way Hello will also match, so this alternation could be made atomic.

  • Improve the vectorization of IndexOfAny. As noted in this post, we now use built-in functions wherever possible, such that improvements to those implicitly benefit Regex as well (in addition to every other workload using them). Our reliance on IndexOfAny is now so high in some regular expressions that it can represent a huge portion of the processing, e.g. on the “regex redux” benchmark shown earlier, ~30% of the overall time is spent in IndexOfAny. There is opportunity here to improve this function, and thereby improve Regex as well. This is covered separately by dotnet/runtime#25023.

  • Prototype a DFA implementation. Certain aspects of the .NET regular expression support are difficult to do with a DFA-based regex engine, but some operations should be doable with minimal concern. For example, Regex.IsMatch doesn’t need to be concerned with capture semantics (.NET has some extra features around captures that make it even more challenging than in other implementations), so if the expression is seen to not contain problematic constructs like backreferences or lookarounds, for IsMatch we could explore employing a DFA-based engine, and possibly that could grow to more widespread use in time.

  • Improve testing. If you’re interested in tests more than in implementation, there are some valuable things to be done here, too. Our code coverage is already very high, but there are still gaps; plugging those (and potentially finding dead code in the process) would be helpful. Finding and incorporating other appropriately licensed test suites to provide more coverage of varying expressions is also valuable.

Thanks for reading. And enjoy!

The post Regex Performance Improvements in .NET 5 appeared first on .NET Blog.

Viewing all 5971 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>