-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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 4 [WIP] #8234
Comments
MSBuild: New, modern terminal build outputWe often get feedback from users that the default MSBuild output (internally known as the console logger) is difficult to parse. It's pretty static, often is a wall of text, and it emits errors as they are triggered during the build instead of logically showing them as part of the project being built. We think these are all great feedback, and are happy to introduce our first iteration on a newer, more modern take on MSBuild output logging. We're calling it Terminal Logger, and it has a few main goals:
Here's what it looks like: The new output can be enabled using
Once enabled, the new logger shows you the restore phase, followed by the build phase. During each phase, the currently-building projects are at the bottom of the terminal, and each building project tells you both the MSBuild Target currently being built, as well as the amount of time spent on that target. We hope this information makes builds less mysterious to users, as well as giving them a place to start searches when they want to learn more about the build! As projects are fully built, a single 'build completed' section is written for each build that captures
There weren't any diagnostics for that example - let's look at another build of the same project where a typo has been introduced: Here you can clearly see the project and typo error outlined. We think that this layout fits the modern feel of .NET and makes use of the capabilities of modern terminals to tell users more about their builds. We hope you'll try it out and provide us feedback on how it works for you, as well as other information you'd like to see here. We hope to use this logger as the foundation for a new batch of UX improvements for MSBuild - including aspects like progress reporting and structured errors in the future! As you use it, please give us feedback via this survey, or via the Discussions section of the MSBuild repository. We look forward to hearing from you all! This work was inspired and begun by Eduardo Villalpando, our intern for the Winter season, who dug into the problem and really helped blaze a trail for the rest of us to follow. It wouldn't have been possible without his help and enthusiasm for the problem! |
SDK: Simplified output path updates
In preview 3 we announced the new simplified output path layout for .NET SDK projects and asked you for your feedback and experiences using the new layout. Thank you for doing so! Your feedback spawned many discussions and based on what we heard from everyone trialing the changes we've made the following updates to the feature:
I want to go into the thought process that led us to these changes. You all overwhelmingly supported removing the
We didn't want people to suddenly have to deal with changes to their We also didn't want to accidentally include the To try out the new version of the feature, we've made it easier to generate the correct Directory.Build.props file: just run <Project>
<!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build -->
<PropertyGroup>
<ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>
</PropertyGroup>
</Project> Please go try out these changes and continue to let us know what you think at our survey for the feature. |
GC: RefreshMemoryLimitIn .NET 8 Preview 4, we have the capability to adjust the memory limit on the fly. What is the problem?In a cloud service scenario, demand comes and goes. To be cost-effective, it is important that our services can scale up and scale down on resource consumption as the demand fluctuates. What is the solution?When we detect a decrease in demand, we can scale down the resource consumption by reducing the memory limit of the service. Previously, this will fail because the GC is unaware of the change and may allocate more memory than the new limit. With this change, you can call Limitations
How to use the API?MethodInfo refreshMemoryLimitMethod = typeof(GC).GetMethod("_RefreshMemoryLimit", BindingFlags.NonPublic | BindingFlags.Static);
refreshMemoryLimitMethod.Invoke(null, Array<object>.Empty); You can also have the GC to refresh some of the GC configuration settings related to the memory limit as follow: AppContext.SetData("GCHeapHardLimit", (ulong)100 * 1024 * 1024);
MethodInfo refreshMemoryLimitMethod = typeof(GC).GetMethod("_RefreshMemoryLimit", BindingFlags.NonPublic | BindingFlags.Static);
refreshMemoryLimitMethod.Invoke(null, Array<object>.Empty); This will set the heap hard limit to 100M. |
Template Engine: secure experience with packages from Nuget.orgIn .NET 8 we're integrating several of NuGet.org's security-related features into the Template Engine, especially in the Improvements
The NuGet team has a plan of record for gradually moving over to a secure-by-default stance. You can read more about their plan, and the timelines involved, in the HTTPS Everywhere blog post. In support of that goal, we're going to start erroring by default when a non-HTTPS source is used. This can be overriden with
|
Libraries: UTF8 improvementsWith .NET 8 Preview 4, we've introduced the new In addition, the new static bool FormatHexVersion(short major, short minor, short build, short revision, Span<byte> utf8Bytes, out int bytesWritten) =>
Utf8.TryWrite(utf8Bytes, CultureInfo.InvariantCulture, $"{major:X4}.{minor:X4}.{build:X4}.{revision:X4}", out bytesWritten); The implementation recognizes The implementation also utilizes the new We expect more UTF8 improvements, including but not limited to improvements to the performance of this functionality, to show up in subsequent .NET 8 previews. |
NuGet: signed package verification on LinuxStarting with .NET 8 Preview 4 SDK, NuGet will verify signed packages on Linux by default. Verification remains enabled on Windows and disabled on macOS. For most Linux users, verification should just work transparently. However, users with an existing root certificate bundle located at Users can opt out of verification by setting the environment variable For more information, see #7688. |
Introducing Time abstractionThe introduction of the TimeProvider abstract class adds time abstraction, which enables time mocking in test scenarios. This functionality is also supported by other features that rely on time progression, such as public abstract class TimeProvider
{
public static TimeProvider System { get; }
protected TimeProvider()
public virtual DateTimeOffset GetUtcNow()
public DateTimeOffset GetLocalNow()
public virtual TimeZoneInfo LocalTimeZone { get; }
public virtual long TimestampFrequency { get; }
public virtual long GetTimestamp()
public TimeSpan GetElapsedTime(long startingTimestamp)
public TimeSpan GetElapsedTime(long startingTimestamp, long endingTimestamp)
public virtual ITimer CreateTimer(TimerCallback callback, object? state,TimeSpan dueTime, TimeSpan period)
}
public interface ITimer : IDisposable, IAsyncDisposable
{
bool Change(TimeSpan dueTime, TimeSpan period);
}
public partial class CancellationTokenSource : IDisposable
{
public CancellationTokenSource(TimeSpan delay, TimeProvider timeProvider)
}
public sealed partial class PeriodicTimer : IDisposable
{
public PeriodicTimer(TimeSpan period, TimeProvider timeProvider)
}
public partial class Task : IAsyncResult, IDisposable
{
public static Task Delay(System.TimeSpan delay, System.TimeProvider timeProvider)
public static Task Delay(System.TimeSpan delay, System.TimeProvider timeProvider, System.Threading.CancellationToken cancellationToken)
public Task WaitAsync(TimeSpan timeout, TimeProvider timeProvider)
public Task WaitAsync(TimeSpan timeout, TimeProvider timeProvider, CancellationToken cancellationToken)
}
public partial class Task<TResult> : Task
{
public new Task<TResult> WaitAsync(TimeSpan timeout, TimeProvider timeProvider)
public new Task<TResult> WaitAsync(TimeSpan timeout, TimeProvider timeProvider, CancellationToken cancellationToken)
} Furthermore, we have made the abstraction available in .NET 8.0 and created a netstandard 2.0 library called Microsoft.Bcl.TimeProvider. This enables the use of the abstraction on supported versions of the .NET Framework and earlier versions of .NET. namespace System.Threading.Tasks
{
public static class TimeProviderTaskExtensions
{
public static Task Delay(this TimeProvider timeProvider, TimeSpan delay, CancellationToken cancellationToken = default)
public static Task<TResult> WaitAsync<TResult>(this Task<TResult> task, TimeSpan timeout, TimeProvider timeProvider, CancellationToken cancellationToken = default)
public static Tasks.Task WaitAsync(this Task task, TimeSpan timeout, TimeProvider timeProvider, CancellationToken cancellationToken = default)
public static CancellationTokenSource CreateCancellationTokenSource(this TimeProvider timeProvider, TimeSpan delay)
}
} Usage Examples // Get System time
DateTimeOffset utcNow= TimeProvider.System.GetUtcNow();
DateTimeOffset localNow = TimeProvider.System.GetLocalNow();
// Create a time provider that work with a time zone different than the local time zone
private class ZonedTimeProvider : TimeProvider
{
private TimeZoneInfo _zoneInfo;
public ZonedTimeProvider(TimeZoneInfo zoneInfo) : base()
{
_zoneInfo = zoneInfo ?? TimeZoneInfo.Local;
}
public override TimeZoneInfo LocalTimeZone { get => _zoneInfo; }
public static TimeProvider FromLocalTimeZone(TimeZoneInfo zoneInfo) => new ZonedTimeProvider(zoneInfo);
}
// Create a time using a time provider
ITimer timer = timeProvider.CreateTimer(callBack, state, delay, Timeout.InfiniteTimeSpan);
// Measure a period using the system time provider
long providerTimestamp1 = TimeProvider.System.GetTimestamp();
long providerTimestamp2 = TimeProvider.System.GetTimestamp();
var period = GetElapsedTime(providerTimestamp1, providerTimestamp2); |
Friendly reminder that we will lock content in by 5/12 EOD. Thank you all! |
System.Runtime.Intrinsics.Vector512 and AVX-512SIMD support has been a staple in .NET for many years, since we first introduced support back in .NET Framework. In .NET Core 3.0, we expanded that support to include the platform specific hardware intrinsics APIs for x86/x64. We expanded that again with support for Arm64 in .NET 5 and then by introducing the cross platform hardware intrinsics in .NET 7. .NET 8 is no exception and is continuing to further our support by introducing
If you have hardware that supports the functionality, then Because of the second and third key features listed above, even if you don't explicitly use This feature has had many Pull Requests go into it and it is the result of the work of many, particularly https://github.com/anthonycanino, https://github.com/DeepakRajendrakumaran, and https://github.com/jkrishnavs. |
Native AOT improvementsWe've updated the default
We also continue improving fundamentals like runtime throughput, memory use, and size on disk with Native AOT. In Preview 4 we're adding a way to communicate an optimization preference such as Speed or Size. The default settings try to strike the right balance between these two, but we're now also introducing a way to specify which way to make tradeoffs. For example, optimizing the result of
The above is the size of a fully self-contained application that includes the runtime (including GC) and all necessary class libraries. In Preview 4, we observed that optimizing for speed produces 2-3% improvements in throughput for real world workloads. |
Linux distro version supportWe previously announced that we were updating our supported Linux distro versions for .NET 8. These changes are included in Preview 4, specifically the .NET 8 is built targeting Ubuntu 16.04, for all architectures. That's primarily important for defining the minimum We are also in the process of updating the .NET 8 Linux build to use clang 16. We expect that change to be included in Preview 5. We won't make a separate announcement for that change. There are no other significant changes. We will continue to support .NET on Linux on Arm32, Arm64, and x64 architectures. |
NuGet: Auditing package dependencies for security vulnerabilities
Enabling security auditingAt any time you wish to receive security audit reports, you can opt-in to the experience by setting the following MSBuild property in a <NuGetAudit>true</NuGetAudit> Additionally, ensure that you have the NuGet.org central registry defined as one of your package sources to retrieve the known vulnerability dataset: <packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources> dotnet add packageWhen you try to add a package that has a known vulnerability, dotnet restoreWhen you restore your packages through Warning codes
Setting a security audit levelYou can set the <NuGetAuditLevel>moderate</NuGetAuditLevel> |
CodegenConsecutive Registers AllocationIn this preview release, we have introduced a new feature in our register allocator called "consecutive register" allocation. Before we delve into the details of what it entails and why it was necessary, let's first review what register allocation is and how it works in RyuJIT. The register allocation algorithm used in RyuJIT is based on a "Linear Scan" approach. It scans the program to identify the lifetime of all variables, referred to as "intervals" in the literature, and assigns a single register to each variable at each use. To determine the best register to assign at a given point, the algorithm needs to identify which variables are live at that point and do not overlap with other variables. It then selects a register from a set of available free registers, using heuristics to determine the best register set at the point of allocation. If no registers are available because they are all assigned to intervals, the algorithm identifies the best register that can be "spilled" and assigned at that location. Spilling involves storing the value of a register on the stack and retrieving it later when needed, which is an expensive operation that the register allocator tries to minimize. Arm64 has two instructions, TBL and TBX, which are used for table vector lookup. These instructions take a "tuple" as one of their operands, which can contain 2, 3, or 4 entities. In PR# 80297, we added two sets of APIs, VectorTableLookup and VectorTableLookupExtension, under the AdvSimd namespace for these instructions. However, these instructions require that all entities in the tuple are present in consecutive registers. To better understand this requirement, let's look at an example. public static Vector128<byte> Test(float f)
{
var a = Produce1();
var b = Produce2();
var c = a + b;
var d = c + a;
var e = d + b;
d = AdvSimd.Arm64.VectorTableLookup((d, e, e, b), c);
} Here is the generated code for the method. movz x0, #0xD1FFAB1E // code for helloworld:Produce1():System.Runtime.Intrinsics.Vector128`1[ubyte]
movk x0, #0xD1FFAB1E LSL #16
movk x0, #0xD1FFAB1E LSL #32
ldr x0, [x0]
blr x0
str q0, [fp, #0x20] // [V01 loc0]
movz x0, #0xD1FFAB1E // code for helloworld:Produce2():System.Runtime.Intrinsics.Vector128`1[ubyte]
movk x0, #0xD1FFAB1E LSL #16
movk x0, #0xD1FFAB1E LSL #32
ldr x0, [x0]
blr x0
ldr q16, [fp, #0x20] // [V01 loc0]
add v17.16b, v16.16b, v0.16b
str q17, [fp, #0x10] // [V03 loc2]
add v16.16b, v17.16b, v16.16b
add v18.16b, v16.16b, v0.16b
mov v17.16b, v18.16b
mov v19.16b, v0.16b
ldr q20, [fp, #0x10] // [V03 loc2]
tbl v16.16b, {v16.16b, v17.16b, v18.16b, v19.16b}, v20.16b
add v0.16b, v0.16b, v16.16b
In the given example, VectorTableLookup() takes a tuple consisting of 4 vectors d, e, e, and b, which are passed in consecutive registers v16 through v19. Even though the 2nd and 3rd value are the same variable e, they are still passed in different registers v17 and v18. This introduces the complexity of finding not only multiple free (or busy) registers (2, 3, or 4) for instructions tbl and tbx, but also consecutive registers. In order to accommodate this new requirement, our algorithm had to be updated at various stages, such as checking ahead of time if consecutive registers are free when assigning a register to the first entity of the tuple, ensuring that assigned registers are consecutive if the variables are already assigned registers and they are not consecutive, and adding stress testing scenarios to handle alternate registers when available. Optimized ThreadStatic field accessAccessing fields that are marked with Arm64We continued improving the code quality of Arm64 and our friends @SwapnilGaikwad and @a74nh at Arm made some good contributions in this release.
Until now, the load/store pair peephole optimization was not performed if one of the values is from local variable. PR #84399 fixed that limitation and enabled the peephole optimization broadly.
|
System.Text.Json: Populating read-only membersStarting with .NET 8 Preview 4, System.Text.Json introduces the ability to deserialize onto read-only properties or fields. We've also introduced option which allows developers to enable it for all properties which are capable of populating - for example custom converters might not be compatible with this feature: JsonSerializerOptions options = new()
{
PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate
}; For existing applications which would like to use this feature but compatibility is a concern this can also be enabled granularly by placing For example, to enable populating for all properties of a specific class: using System.Text.Json;
using System.Text.Json.Serialization;
JsonSerializerOptions options = new()
{
WriteIndented = true,
// Instead of granular control we could also enable this globally like this:
// PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate
};
CustomerInfo customer = JsonSerializer.Deserialize<CustomerInfo>("""{"Person":{"Name":"John"},"Company":{"Name":"John and Son"}}""", options)!;
Console.WriteLine(JsonSerializer.Serialize(customer, options));
class PersonInfo
{
// there is nothing here to be populated since string cannot be re-used
public required string Name { get; set; }
public string? Title { get; set; }
}
class CompanyInfo
{
public required string Name { get; set; }
public string? Address { get; set; }
public string? PhoneNumber { get; set; }
public string? Email { get; set; }
}
// notes:
// - attribute does not apply to the `CustomerInfo` class itself: i.e. properties of type `CustomerInfo` wouldn't be auto-populated
// - automatic rules like these can be implemented with contract customization
// - attribute do apply to `Person` and `Company` properties
// - attribute can also be placed on individual properties
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class CustomerInfo
{
private const string NA = "N/A";
// note how neither of these have setters
public PersonInfo Person { get; } = new PersonInfo() { Name = "Anonymous", Title = "Software Developer" };
public CompanyInfo Company { get; } = new CompanyInfo() { Name = NA, Address = NA, PhoneNumber = NA, Email = NA };
} The output of above is identical output to that which would be achieved by global option: {
"Person": {
"Name": "John",
"Title": "Software Developer"
},
"Company": {
"Name": "John and Son",
"Address": "N/A",
"PhoneNumber": "N/A",
"Email": "N/A"
}
} for comparison previously we'd see our input but since there was no settable property {
"Person": {
"Name": "Anonymous",
"Title": "Software Developer"
},
"Company": {
"Name": "N/A",
"Address": "N/A",
"PhoneNumber": "N/A",
"Email": "N/A"
}
} Other notes
|
System.Text.Json Improvements
|
CodeGenCommunity PRs (Many thanks to JIT community contributors!)
Code vectorizationJIT/NativeAOT can now unroll and auto-vectorize various memory operations such as comparison, copying and zeroing with SIMD (including AVX-512 instructions on x64!) if it can determinate their sizes in compile time:
bool CopyFirst50Items(ReadOnlySpan<int> src, Span<int> dst) =>
src.Slice(0, 50).TryCopyTo(dst); ; Method CopyFirst50Items
push rbp
vzeroupper
mov rbp, rsp
cmp edx, 50 ;; src.Length >= 50 ?
jb SHORT G_M1291_IG05
xor eax, eax
cmp r8d, 50 ;; dst.Length >= 50 ?
jb SHORT G_M1291_IG04
vmovdqu zmm0, zmmword ptr [rsi]
vmovdqu zmm1, zmmword ptr [rsi+40H]
vmovdqu zmm2, zmmword ptr [rsi+80H]
vmovdqu xmm3, xmmword ptr [rsi+B8H]
vmovdqu zmmword ptr [rcx], zmm0
vmovdqu zmmword ptr [rcx+40H], zmm1
vmovdqu zmmword ptr [rcx+80H], zmm2
vmovdqu xmmword ptr [rcx+B8H], xmm3
mov eax, 1
G_M1291_IG04:
pop rbp
ret
G_M1291_IG05:
call [System.ThrowHelper:ThrowArgumentOutOfRangeException()]
int3
; Total bytes of code: 96 Here JIT used 3 ZMM (AVX-512) registers to perform memmove-like operation inlined (even if src and dst overlap). A similar codegen will be generated for compile-time constant data e.g. utf8 literals:
General Optimizations
|
What's new in .NET 8 Preview 4
This issue is for teams to highlight work for the community that will release in .NET 8 Preview 4
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
Optional
Below are three additional items to consider. These will help the .NET 8 blog team and the community throughout the release.
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
The text was updated successfully, but these errors were encountered: