Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's new in .NET 8 Preview 6 [WIP] #8437

Closed
3 tasks
leecow opened this issue May 3, 2023 · 14 comments
Closed
3 tasks

What's new in .NET 8 Preview 6 [WIP] #8437

leecow opened this issue May 3, 2023 · 14 comments
Assignees

Comments

@leecow
Copy link
Member

leecow commented May 3, 2023

What's new in .NET 8 Preview 6

This issue is for teams to highlight work for the community that will release in .NET 8 Preview 6

To add content, use a new conversation entry. The entry should include the team name and feature title as the first line shown in the template below.

Required

## Team Name: Feature title

[link to the tracking issue or epic item for the work]

Tell the story of the feature and anything the community should pay particular attention 
to be successful in using the feature.

Optional

Below are three additional items to consider. These will help the .NET 8 blog team and the community throughout the release.

  • Link to documentation.
    • Where can we send people to learn more about the feature?
  • Link to where you'd like people to provide feedback for this feature.
    • Where can people best comment on the direction of this feature?
  • Whether you would like assistance from the .NET 8 writers to craft a story for this feature.

Index of .NET 8 releases

Preview 1: #8133
Preview 2: #8134
Preview 3: #8135
Preview 4: #8234
Preview 5: #8436
Preview 6: #8437
Preview 7: #8438
RC 1: #8439
RC 2: #8440

@vcsjones
Copy link
Member

vcsjones commented Jun 15, 2023

System.Security: SHA-3 Support

dotnet/runtime#20342

In .NET 8 Preview 6, support for the SHA-3 hashing primitives is now available on platforms that offer SHA-3. This is currently Linux with OpenSSL 1.1.1 or later, and Windows 11 Build 25324 or later.

APIs where SHA-2 is available now offer a SHA-3 compliment. This includes SHA3_256, SHA3_384, and SHA3_512 for hashing; HMACSHA3_256, HMACSHA3_384, and HMACSHA3_512 for HMAC; HashAlgorithmName.SHA3_256, HashAlgorithmName.SHA3_384, and HashAlgorithmName.SHA3_512 for hashing where the algorithm is configurable; and RSAEncryptionPadding.OaepSHA3_256, RSAEncryptionPadding.OaepSHA3_384, and RSAEncryptionPadding.OaepSHA3_512 for RSA OAEP encryption.

Using the SHA-3 APIs is similar to SHA-2, with the addition of an IsSupported property to determine if the platform offers SHA-3.

// Hashing example
// Check if SHA-3-256 is supported on the current platform.
if (SHA3_256.IsSupported)
{
    byte[] hash = SHA3_256.HashData(dataToHash);
}
else
{
    // Determine application behavior if SHA-3 is not available.
}

// Signing Example
// Check if SHA-3-256 is supported on the current platform.
if (SHA3_256.IsSupported)
{
     using ECDsa ec = ECDsa.Create(ECCurve.NamedCuves.nistP256);
     byte[] signature = ec.SignData(dataToBeSigned, HashAlgorithmName.SHA3_256);
}
else
{
    // Determine application behavior if SHA-3 is not available.
}

Additionally, SHA-3 includes two extendable-output functions (XOFs), SHAKE128 and SHAKE256. These are available as Shake128 and Shake256.

if (Shake128.IsSupported)
{
    using Shake128 shake = new Shake128();
    shake.AppendData("Hello .NET!"u8);
    byte[] digest = shake.GetHashAndReset(outputLength: 32);
    
    // Also available as a one-shot:
    digest = Shake128.HashData("Hello .NET!"u8, outputLength: 32);
}
else
{
    // Determine application behavior if SHAKE is not available.
}

SHA-3 support is currently aimed at supporting cryptographic primitives. Higher level constructions and protocols are not expected to fully support SHA-3 initially. This includes, but is not limited to, X.509 certificates, SignedXml, and COSE. Support for SHA-3 may expand in the future depending on platform support and standards which adopt SHA-3.

SHA-3 was standardized by NIST as FIPS 202 as an alternative to, not successor, to SHA-2. Developers and organizations should decide when or even if adopting SHA-3 is appropriate for them.

@ilonatommy
Copy link
Member

ilonatommy commented Jun 20, 2023

HybridGlobalization mode on WASM

WASM app can use a new globalization mode that brings lighter ICU bundle and leverages Web API instead. In Hybrid mode, globalization data is partially pulled from ICU bundle and partially from calls into JS. It serves all the locales supported by WASM.

When to consider using HybridGlobalization:

This option is most suitable for applications that cannot work in InvariantGlobalization mode and use localization data from more than one ICU shard (EFIGS, CJK, no-CJK) - so currently using either:

<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>

in Blazor WebAssembly or

<WasmIncludeFullIcuData>true</WasmIncludeFullIcuData>

in WASM Browser.

Apps that were loading no-CJK or CJK shard using the custom ICU file loading method:

<WasmIcuDataFileName>icudt_no_CJK.dat</WasmIcuDataFileName>

might also be interested, because hybrid file is smaller than no-CJK shard by 26% and smaller than CJK by 15%.

How to use HybridGlobalization:

Set MsBuild property: <HybridGlobalization>true</HybridGlobalization>. It will load icudt_hybrid.dat file that is 46% smaller than originally loaded icudt.dat.

Limitations:

Due to limitations of Web API, not all Globalization APIs are supported in Hybrid mode. Some of the supported APIs changed their behavior. To make sure your application is will not be affected, read the section Behavioral differences for WASM.

APIs that obtain the result by calling into JS have worse performance than the non-hybrid version. These APIs are listed in the documentation. The APIs that are not on the "Affected public APIs" lists perform the same as in non-hybrid mode.

@tarekgh
Copy link
Member

tarekgh commented Jun 24, 2023

Expanding LoggerMessageAttribute Constructor Overloads for Enhanced Functionality

The LoggerMessageAttribute previously offered only two constructors:

    public LoggerMessageAttribute()
    public LoggerMessageAttribute(int eventide, LogLevel level, string message)

a parameter-less constructor and another constructor that required specifying all the parameters (int eventide, LogLevel level, string message). This limitation meant that users had to either provide all the parameters, even if they didn't need them, using the second constructor, or use the parameter-less constructor and then supply the necessary parameters through attribute properties, such as [LoggerMessage(Message = "A record is being skipped: {recordId}")].
To address this issue, additional constructor overloads have been introduced, offering greater flexibility in specifying the required parameters with reduced code. The new constructor overloads include options such as specifying only the LogLevel and message, only the LogLevel, or only the message. These enhancements make it easier for users to define LoggerMessageAttributes while minimizing unnecessary code.

    public LoggerMessageAttribute(LogLevel level, string message);
    public LoggerMessageAttribute(LogLevel level);
    public LoggerMessageAttribute(string message);

Example

        [LoggerMessage(Level = LogLevel.Warning, Message = "{p1} should be valid")]
        public partial void LogWaraning(string p1);

dotnet/runtime#87254

Note: In the upcoming preview, for constructors that do not require an event Id, the system will automatically generate the event Id, eliminating the need for users to manually provide it.

@tarekgh
Copy link
Member

tarekgh commented Jun 24, 2023

Metric APIs change

InstrumentRecorder

In Preview 5, we introduced a class called InstrumentRecorder, primarily designed as a helper class for test scenarios. However, in Preview 6, we made significant improvements to this class and relocated it to a different library called Microsoft.Extensions.Telemetry.Testing. Along with the relocation, we also renamed the class to MetricCollector.
These enhancements in Preview 6 now allow the MetricCollector class to record metric measurements along with timestamps. Additionally, the class offers the flexibility to use any desired time provider for accurate timestamp generation.

Example

        const string CounterName = "MyCounter";

        var now = DateTimeOffset.Now;

        var timeProvider = new FakeTimeProvider(now);
        using var meter = new Meter(Guid.NewGuid().ToString());
        var counter = meter.CreateCounter<long>(CounterName);
        using var collector = new MetricCollector<long>(counter, timeProvider);

        Assert.Empty(collector.GetMeasurementSnapshot());
        Assert.Null(collector.LastMeasurement);

        counter. Add(3);

        // verify the update was recorded
        Assert.Equal(counter, collector.Instrument);
        Assert.NotNull(collector.LastMeasurement);
        
        Assert.Single(collector.GetMeasurementSnapshot());
        Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement);
        Assert.Equal(3, collector.LastMeasurement.Value);
        Assert.Empty(collector.LastMeasurement.Tags);
        Assert.Equal(now, collector.LastMeasurement.Timestamp);        

AddMetrics extension method namespace

To ensure consistency with the other extension methods in IServiceCollection, the namespace of the AddMetrics method has been modified from Microsoft.Extensions.Diagnostics.Metrics to Microsoft.Extensions.DependencyInjection. This change allows for a more unified and streamlined approach when using the IServiceCollection extension methods.

- namespace Microsoft.Extensions.Diagnostics.Metrics
+ namespace Microsoft.Extensions.DependencyInjection
{
    public static class MetricsServiceExtensions
    {
        public static IServiceCollection AddMetrics(this IServiceCollection services)
    }
}

@tarekgh
Copy link
Member

tarekgh commented Jun 24, 2023

Introducing the options validation source generator

To reduce startup overhead and improve validation feature set, we've introduced the source code generator that implements the validation logic.

Example

public class FirstModelNoNamespace
{
    [Required]
    [MinLength(5)]
    public string P1 { get; set; } = string. Empty;

    [Microsoft.Extensions.Options.ValidateObjectMembers(typeof(SecondValidatorNoNamespace))]
    public SecondModelNoNamespace? P2 { get; set; }

    [Microsoft.Extensions.Options.ValidateObjectMembers]
    public ThirdModelNoNamespace? P3 { get; set; }
}

public class SecondModelNoNamespace
{
    [Required]
    [MinLength(5)]
    public string P4 { get; set; } = string. Empty;
}

public class ThirdModelNoNamespace
{
    [Required]
    [MinLength(5)]
    public string P5 { get; set; } = string.Empty;
}

[OptionsValidator]
public partial class FirstValidatorNoNamespace : IValidateOptions<FirstModelNoNamespace>
{
}

[OptionsValidator]
public partial class SecondValidatorNoNamespace : IValidateOptions<SecondModelNoNamespace>
{
}

This will generate code like the following:

partial class FirstValidatorNoNamespace
{
    /// <summary>
    /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
    /// </summary>
    /// <param name="name">The name of the options instance being validated.</param>
    /// <param name="options">The options instance.</param>
    /// <returns>Validation result.</returns>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
    public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::FirstModelNoNamespace options)
    {
        var baseName = (string.IsNullOrEmpty(name) ? "FirstModelNoNamespace" : name) + ".";
        var builder = new global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder();
        var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
        var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
        var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(2);

        context.MemberName = "P1";
        context.DisplayName = baseName + "P1";
        validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
        validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
        if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P1!, context, validationResults, validationAttributes))
        {
            builder.AddResults(validationResults);
        }

        if (options.P2 is not null)
        {
            builder.AddResult(global::__OptionValidationStaticInstances.__Validators.V1.Validate(baseName + "P2", options.P2));
        }

        if (options.P3 is not null)
        {
            builder.AddResult(global::__ThirdModelNoNamespaceValidator__.Validate(baseName + "P3", options.P3));
        }

        return builder. Build();
    }
}


partial class SecondValidatorNoNamespace
{
    /// <summary>
    /// Validates a specific named options instance (or all when <paramref name="name"/> is <see langword="null" />).
    /// </summary>
    /// <param name="name">The name of the options instance being validated.</param>
    /// <param name="options">The options instance.</param>
    /// <returns>Validation result.</returns>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
    public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::SecondModelNoNamespace options)
    {
        var baseName = (string.IsNullOrEmpty(name) ? "SecondModelNoNamespace" : name) + ".";
        var builder = new global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder();
        var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
        var validationResults = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationResult>();
        var validationAttributes = new global::System.Collections.Generic.List<global::System.ComponentModel.DataAnnotations.ValidationAttribute>(2);

        context.MemberName = "P4";
        context.DisplayName = baseName + "P4";
        validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1);
        validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A2);
        if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4!, context, validationResults, validationAttributes))
        {
            builder.AddResults(validationResults);
        }

        return builder. Build();
    }
}

If the app is using dependency injection, it can easily inject the validation there.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.Configure<FirstModelNoNamespace>(builder.Configuration.GetSection(...));

builder.Services.AddSingleton<IValidateOptions<FirstModelNoNamespace>, FirstValidatorNoNamespace>();
builder.Services.AddSingleton<IValidateOptions<SecondModelNoNamespace>, SecondValidatorNoNamespace>();

dotnet/runtime#85475

@jkoritzinsky
Copy link
Member

jkoritzinsky commented Jun 26, 2023

Interop: Source Generated COM Interop

In .NET 8 Preview 6, we have shipped a new source generator that supports interoperating with COM interfaces using the source generated interop story that we started with LibraryImportAttribute. In .NET 8 Preview 6, you can use the System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute to mark an interface as a COM interface for the source generator. The source generator will then generate code to enable calling from C# code to unmanaged code, as well as code to enable calling from unmanaged code into C#. This source generator integrates with LibraryImportAttribute, and you can use types with the GeneratedComInterfaceAttribute as parameters and return types in LibraryImportAttribute-attributed methods.

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

[GeneratedComInterface]
[Guid("5401c312-ab23-4dd3-aa40-3cb4b3a4683e")]
interface IComInterface
{
    void DoWork();
}

internal class MyNativeLib
{
    [LibraryImport(nameof(MyNativeLib))]
    public static partial void GetComInterface(out IComInterface comInterface);
}

The source generator also supports the new System.Runtime.InteropServices.Marshalling.GeneratedComClassAttribute to enable you to pass your types that implement interfaces with the System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute-attributed interfaces to unmanaged code. The source generator will generate the code necessary to expose a COM object that implements the interfaces and forwards calls to the managed implementation.

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

MyNativeLib.GetComInterface(out IComInterface comInterface);
comInterface.RegisterCallbacks(new MyCallbacks());
comInterface.DoWork();

[GeneratedComInterface]
[Guid("5401c312-ab23-4dd3-aa40-3cb4b3a4683e")]
interface IComInterface
{
    void DoWork();
    void RegisterCallbacks(ICallbacks callbacks);
}

[GeneratedComInterface]
[Guid("88470b56-aabc-46af-bf97-8106a1aa3cf9")]
interface ICallbacks
{
    void Callback();
}

internal class MyNativeLib
{
    [LibraryImport(nameof(MyNativeLib))]
    public static partial void GetComInterface(out IComInterface comInterface);
}

[GeneratedComClass]
internal class MyCallbacks : ICallbacks
{
    public void Callback()
    {
        Console.WriteLine("Callback called");
    }
}

Methods on interfaces with the GeneratedComInterfaceAttribute support all the same types as LibraryImportAttribute, and LibraryImportAttribute gains support for GeneratedComInterface-attributed types and GeneratedComClass-attributed types in this release.

If your C# code will only be use a GeneratedComInterfaceAttribute-attributed interface to either wrap a COM object from unmanaged code or wrap a managed object from C# to expose to unmanaged code, you can use the options in the GeneratedComInterfaceAttribute.Options property to customize which code will be generated. This option will enable you to not need to write marshallers for scenarios that you know will not be used.

The source generator uses the new System.Runtime.InteropServices.Marshalling.StrategyBasedComWrappers type to create and manage the COM object wrappers and the managed object wrappers. This new type handles providing the expected .NET user experience for COM interop, while providing customization points for advanced users. If your application has its own mechanism for defining types from COM or if you need to support scenarios that source-generated COM does not currently support, you can consider using the new StrategyBasedComWrappers type to add the missing features for your scenario and get the same .NET user experience for your COM types.

How to Use

Like LibraryImportAttribute, the COM source generator provides an easy onboarding experience through analyzers and code fixers. Next to each interface that has the System.Runtime.InteropServices.ComImportAttribute, a lightbulb will offer an option to convert to source generated interop. This fix will change the interface to use the GeneratedComInterfaceAttribute. Next to every class that implements an interface with the GeneratedComInterfaceAttribute, a lightbulb will offer an option to add the GeneratedComClassAttribute to the type. Once your types are converted, you can move your DllImport methods to use LibraryImportAttribute with the existing code fixer there. With these two lightbulbs, it is easy to convert your existing COM interop code to use the new source generated interop. There are also more analyzers to help catch places where you may be mixing source-generated and runtime-based COM interop that may require additional work.

Limitations

Currently the COM source generator has the following limitations. We do not expect to address these limitations in .NET 8, but we may in a future version of .NET.

  • No support for IDispatch-based interfaces.
    • Support for these interfaces may be manually implemented using a local definition of the IDispatch interface.
  • No support for IInspectable-based interfaces.
    • Use the CsWinRT tool to generate the interop code for these interfaces.
  • No support for apartment affinity.
    • All COM objects are assumed to be free-threaded. Support for apartment affinity may be manually implemented using the StrategyBasedComWrappers type and custom strategy implementations.
  • No support for COM properties.
    • These may be manually implemented as methods on the interface.
  • No support for COM events.
    • These may be manually implemented using the underlying COM APIs.
  • No support for using the new keyword to activate a COM CoClass.
    • Use LibraryImportAttribute to P/Invoke to the CoCreateInstance API to activate the CoClass.

Usage in .NET

We have already moved the .NET support for distributed transactions through the System.Transactions library to use the new source-generated interop! We used this experience to help refine the analyzers and code-fixers to provide a good migration experience.

@carlossanlop
Copy link
Member

carlossanlop commented Jun 30, 2023

Stream-based ZipFile CreateFromDirectory and ExtractToDirectory method overloads

We added new overloads of ZipFile.CreateFromDirectory that allow users to collect all the files included in a directory and zip them, then store the resulting zip file into the provided stream.

Symmetrically, we added the ZipFile.ExtractToDirectory overloads that allow users to provide a stream containing a zipped file and extract its contents into the filesystem.

These APIs can avoid having to use the disk as an intermediate step. This can be useful in scenarios where disk space is constrained, like for example, in cloud-based environments:

  • CreateFromDirectory does not have to write the zipped result into disk.
  • ExtractToDirectory does not require the zipped file to be located in disk.

APIs

namespace System.IO.Compression;

public static partial class ZipFile
{
    public static void CreateFromDirectory(string sourceDirectoryName, Stream destination);
    public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, CompressionLevel compressionLevel, bool includeBaseDirectory);
    public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, CompressionLevel compressionLevel, bool includeBaseDirectory, Encoding? entryNameEncoding);

    public static void ExtractToDirectory(Stream source, string destinationDirectoryName) { }
    public static void ExtractToDirectory(Stream source, string destinationDirectoryName, bool overwriteFiles) { }
    public static void ExtractToDirectory(Stream source, string destinationDirectoryName, Encoding? entryNameEncoding) { }
    public static void ExtractToDirectory(Stream source, string destinationDirectoryName, Encoding? entryNameEncoding, bool overwriteFiles) { }
}

Usage

Here are a couple of examples using the overloads that take all the arguments:

Stream destinationStream = GetStreamFromSomewhere();

ZipFile.CreateFromDirectory(
    sourceDirectoryName: "/home/username/sourcedirectory/",
    destination: destinationStream,
    compressionLevel: CompressionLevel.Optimal,
    includeBaseDirectory: true,
    entryNameEncoding: Encoding.UTF8);
Stream sourceStream = GetStreamFromSomewhere();

ZipFile.ExtractToDirectory(
    source: sourceStream,
    destinationDirectoryName: "/home/username/destinationdirectory/",
    entryNameEncoding: Encoding.UTF8,
    overwriteFiles: true);

dotnet/runtime#1555

@layomia
Copy link

layomia commented Jul 3, 2023

Configuration binding source generator improvements

In Preview 3, we introduced a new source generator to provide AOT and trim-friendly configuration in ASP.NET Core. The generator is an alternative to the pre-exising reflection-based implementation.

When it was first shipped, community feedback revealed some important bugs that largely prevented usage. Since then, we've made several improvements and the generator is ready for folks to give it another go using Preview 6.

An example app that uses configuration binding and is published with AOT goes from having two (2) AOT analysis warnings during compilation to having none. The app would fail when executed, but now it works. This sample adds configuration binding to the template SDK project for building minimal API apps for AOT.

dotnet new api --aot

For the upcoming Preview 7, we've made changes to improve size. The sample app drops by ~839.68 kB from 10.19 MB to 9.34 MB when published with AOT.

No source code changes are needed to use the generator. It's enabled by default in AOT'd web apps. For other project types it is off by default, but you can control it by adding the following property to your project.

<PropertyGroup>
    <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>

Please try the source generator in AOT apps that perform configuration, and report any issues at https://github.com/dotnet/runtime/issues. You can find known issues here.

@JulieLeeMSFT
Copy link
Member

JulieLeeMSFT commented Jul 5, 2023

CodeGen

Community PRs (Many thanks to JIT community contributors!)

  • @huoyaoyuan made use of ECMA-335 III.4.6 to optimize type checks involving Nullable<T>, PR#87241. @huoyaoyuan also made a fix for parameter ordering and value in gtNewColonNode in PR#87366.

  • @jasper-d optimized new Vector3 { X = val1, Y = val2, Z = val3 } pattern to emit the same codegen as Vector3 { val1,val2, val3} by folding cost WithElement to CNS_VEC, PR#84543.

Vector3 Foo1() => new Vector3 { X = 1, Y = 2, Z = 3 }; 
Vector3 Foo2() => new Vector3(1, 2, 3); 
  • @pedrobsaila fused numeric < and == comparisons to <= and > and == to >= and reduced the number of instructions in #PR78786.

  • @SingleAccretion has been working on internal JIT cleanup to greatly simplify the Internal Representation (IR). He removed GT_ASG IR that we had for many years, and it shows about 1 ~ 1.5% of throughput improvements, PR#85871.

image

AVX-512

Since we enabled Vector512 and AVX512 support in Preview 4, our community members continued to add new features.

  • @DeepakRajendrakumaran accelerated Vector256/512.Shuffle() using AVX512 VBMI that improves performance by reducing data movement from memory to registers and rearranging data in the two memory addresses, PR#87083.

  • @Ruihan-Yin enabled EVEX feature that embeds broadcast and optimized Vector256.Add() when vector base type is TYP_FLOAT, PR#84821.

Arm64

  • @SwapnilGaikwad used Conditional Invert (cinv) and Conditional Negate (cneg) instead of Conditional Select(csel) that reduced code size and speed on Arm64 platform, PR#84926.
image
  • @SwapnilGaikwad also combined two vector table lookups into one on Arm64 that shows 10-50% of speed-up on microbenchmarks such as FormatterInt32, PR#87126.

  • We changed register allocation to iterate over the registers of interest instead of over all the registers, which improved the JIT’s own throughput for various scenarios by up to 5% on Arm64 and around 2% on x64.

@ivanpovazan
Copy link
Member

ivanpovazan commented Jul 6, 2023

Support for targeting iOS-like platforms with NativeAOT as an opt-in feature

dotnet/runtime#80905

In .NET 8 Preview 6, we are shipping NativeAOT with support for targeting iOS-like platforms. This includes building and running .NET iOS and .NET MAUI applications with NativeAOT on: ios, iossimulator, maccatalyst, tvos or tvossimulator systems.
The motivation of this work is to enable our customers to explore the possibility of achieving better performance and size savings when targeting such platforms with NativeAOT.

This is available as an opt-in feature intended for app deployment, while Mono is still used as the default runtime choice for app development and deployment.
This milestone has been reached with a great collaboration between our community members: @filipnavara @AustinWise and @am11 who contributed with their work, and joint effort of NativeAOT, Mono and Xamarin teams.

Current state

The current state has been tested with:

  • .NET iOS app (dotnet new ios)
  • .NET MAUI iOS app (dotnet new maui)

These sample applications show the following preliminary results compared to Mono:

.NET iOS app Mono-p6 NativeAOT-p6 diff (%)
Size on disk (Mb) 11,61 6,99 -40%
.ipa (Mb) 4,37 2,69 -39%
.NET MAUI iOS app Mono-p6 NativeAOT-p6 diff (%) NativeAOT-fix diff (%)
Size on disk (Mb) 40,24 50,13 25% 27,58 -31,46%
.ipa (Mb) 14,68 16,59 13% 10,23 -30,32%

The .NET 8 Preview 6 results (marked as *-p6) show that the .NET iOS app has significant improvements compared to Mono where the compressed app bundle (.ipa) is up to ~39% smaller showing great potential, while the .NET MAUI iOS app shows worse results producing ~13% larger output. However, we have identified the root cause of the size regression with the .NET MAUI app and we are currently working on the following list of issues to address the size regression:

  1. Exclude assemblies from NativeAOT app bundles xamarin/xamarin-macios#18532
  2. NativeAOT: investigate if we want _AggressiveAttributeTrimming set for NativeAOT xamarin/xamarin-macios#18479
  3. Fix how System.Linq.Expressions.dll is supported on iOS-like platforms runtime#87924
  4. Trimming and extensions compatibility design – discussion runtime#86649

By fixing the identified issues 1-3) we have estimated that NativeAOT can reach great results with the .NET MAUI app, which is shown in the column NativeAOT-fix, where the size of the application bundle is ~30% smaller compared to Mono. Fixing the issue 4) would potentially improve the performance even further, but at this stage we cannot estimate the exact numbers. More information about .NET MAUI performance with NativeAOT is being tracked in: dotnet/runtime#80907

We would like to point out that conclusions regarding NativeAOT performance on iOS-like platforms should not be drawn out from the presented numbers in the table nor from the .NET 8 Preview 6 release in general. Especially since this is still work-in-progress and only the first step towards making the feature ready for .NET 9 official release. Therefore, we are actively working on improvements and identifying all the work that will try to bring full NativeAOT experience to our customers towards achieving great performance and size savings, which is being tracked in the following list of issues (and their subtasks):

How to build and run a .NET MAUI application with NativeAOT on an iOS device with .NET CLI

Installation

dotnet workload install maui

Creating a sample application

dotnet new maui -n HelloMaui

Choosing NativeAOT over Mono

The MSBuild properties PublishAot=true and PublishAotUsingRuntimePack=true (temporary, see below) enable NativeAOT deployments.

These two properties are the only notable difference compared to when deploying with Mono.
You need to add them in a PropertyGroup of the project file of your application:

<PropertyGroup>
    <PublishAot>true</PublishAot>
    <PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
</PropertyGroup>

Which means that every time the app is deployed via dotnet publish it will be deployed by using NativeAOT.

Running your application

dotnet publish -f net8.0-ios -c Release -r ios-arm64  /t:Run

iOS and NativeAOT compatibility

Not all features in iOS are compatible with NativeAOT. Similarly, not all libraries commonly used in iOS are compatible with NativeAOT. .NET 8 represents the start of work to enable NativeAOT for iOS, your feedback will help guide our efforts during .NET 8 previews and beyond, to ensure we focus on the places where the benefits of NativeAOT can have the largest impact.

The following list includes some limitations when targeting iOS-like platforms that have been encountered so far (and thus might not be the final list):

  • Installation and app deployment by using Visual Studio have not been tested yet
  • Using NativeAOT is only enabled during app deployment - dotnet publish
  • Linq.Expressions library functionality is not fully supported yet
  • The PublishAotUsingRuntimePack=true MSBuild property is a temporary workaround required for targeting iOS-like platforms with NativeAOT
  • Managed code debugging is only supported with Mono

NOTE: The previous list is an extension to the limitations applicable for all platforms with NativeAOT: https://github.com/dotnet/runtime/blob/main/src/coreclr/nativeaot/docs/limitations.md

Final note

We would like to invite everyone to try out this new feature and file any discovered issues to help us improve the user experience further.

Please note that Mono AOT which offers more compatibility with dynamic code is still going to be the primary deployment option and we’re not trying to remove dynamic code - just offer an option to people who don’t need dynamic code compatibility and don’t want the app size/memory/startup implications caused by it.

@wfurt
Copy link
Member

wfurt commented Jul 6, 2023

Support for HTTPS proxy

dotnet/runtime#31113

While HttpClient supported various proxy types for a while, they all allows man-in-the-middle to see what site the client is connecting to. (even for HTTPS URIs) HTTPS proxy allows to create encrypted channel between client and the proxy so all the subsequent request can be handled with full privacy.

How to Use

for Unix
export all_proxy=https://x.x.x.x:3218 and set all_proxy=https://x.x.x.x:3218 for Windows. This can be also controlled programmatically via WebProxy class.

@eiriktsarpalis
Copy link
Member

eiriktsarpalis commented Jul 7, 2023

System.Text.Json improvements

Preview 6 sees the inclusion of a number of improvements and reliability fixes for the System.Text.Json source generator aimed for making the Native AOT experience on par with the reflection-based serializer:

JsonStringEnumConverter<TEnum> dotnet/runtime#87224

This new converter complements the existing JsonStringEnumConverter class, which is not supported in Native AOT. Users wishing to target Native AOT users should annotate their enum types using the new converter like so:

[JsonConverter(typeof(JsonStringEnumConverter<MyEnum>))]
public enum MyEnum { Value1, Value2, Value3 }

[JsonSerializable(typeof(MyEnum))]
public partial class MyContext : JsonSerializerContext { }

JsonConverter.Type dotnet/runtime#87382

The new property allows users to look up the type of a non-generic JsonConverter instance:

Dictionary<Type, JsonConverter> CreateDictionary(IEnumerable<JsonConverter> converters)
    => converters.Where(converter => converter.Type != null).ToDictionary(converter => converter.Type!);

It should be noted that the property is nullable since it returns null for JsonConverterFactory instances and typeof(T) for JsonConverter<T> instances.

@baronfel
Copy link
Member

baronfel commented Jul 7, 2023

SDK: Containers performance and compatibility

Over the course of the preview 6 time frame we invested in improving the performance of the Container pushes to remote registries, the reliability of the push operation, and support for a larger range of registries. At the same time, Tom Desyn of Red Hat was busy improving our support for a range of technologies prevalent in that ecosystem, like Podman, the Quay.io registry, and more.

This version should see much improved performance for container pushes, especially to Azure registries. This is because of our improved support for pushing layers in one operation. In addition, for registries that don't support atomic uploads, we've implemented a more reliable and re-try-able chunking upload mechanism.

As a side effect of these changes, we also expanded our support matrix for registries. Harbor and Artifactory join the list of known-working registries, and Tom's work enabled Quay.io and Podman pushes as well.

We've also made a few changes to the generated image defaults for .NET 8 - we now default to using the new Rootless capability of the Microsoft .NET containers, helping your applications stay secure-by-default. You can change this at any time by setting your own ContainerUser. We also changed the default container tag to be latest - keeping in sync with other tooling in the containers space, and making it easier to use in your inner development loops.

@JonDouglas JonDouglas self-assigned this Jul 9, 2023
@JonDouglas
Copy link
Collaborator

@dotnet dotnet deleted a comment from asaelperez614 Jul 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests