I built 2.0 coreclr, corefx, and core-setup repos from scratch on a linux machine. In the Corefx build output directory, there are multiple copies of dll's with the same name. Some are obviously targeting different environments (Unix/AnyOS or netstandard/netcoreapp) and some dlls are not fully processed(pregenfacades, pretrim, etc.). However, I still don't quiet understand where the size differences come from:
-rw-r--r-- 1 29K Jul 7 06:15 ./AnyOS.AnyCPU.Release/netfx/netcoreapp/System.Runtime.dll
-rw-r--r-- 1 29K Jul 7 06:15 ./AnyOS.AnyCPU.Release/netstandard/netstandard/System.Runtime.dll
-rw-r--r-- 1 242K Sep 8 04:38 ./obj/ref/System.Runtime/4.2.0.0/netcoreapp/System.Runtime.dll
-rw-r--r-- 1 17K Sep 8 04:41 ./obj/Unix.AnyCPU.Release/System.Runtime/netcoreapp/PreGenFacades/System.Runtime.dll
-rw-r--r-- 1 33K Sep 8 04:41 ./obj/Unix.AnyCPU.Release/System.Runtime/netcoreapp/PreTrim/System.Runtime.dll
-rw-r--r-- 1 33K Sep 8 04:41 ./obj/Unix.AnyCPU.Release/System.Runtime/netcoreapp/System.Runtime.dll
-rw-r--r-- 2 242K Sep 8 04:38 ./ref/netcoreapp/System.Runtime.dll
-rw-r--r-- 2 29K Jul 7 06:15 ./ref/netfx/System.Runtime.dll
-rw-r--r-- 2 29K Jul 7 06:15 ./ref/netstandard/System.Runtime.dll
-rw-r--r-- 2 242K Sep 8 04:38 ./ref/System.Runtime/4.2.0.0/netcoreapp/System.Runtime.dll
-rw-r--r-- 3 33K Sep 8 04:41 ./runtime/netcoreapp-Linux-Release-x64/System.Runtime.dll
-rw-r--r-- 3 33K Sep 8 04:41 ./testhost/netcoreapp-Linux-Release-x64/shared/Microsoft.NETCore.App/9.9.9/System.Runtime.dll
-rw-r--r-- 3 33K Sep 8 04:41 ./Unix.AnyCPU.Release/System.Runtime/netcoreapp/System.Runtime.dll
Notice that some dlls are of size 240KB and others range from 17KB to 33KB. Using dotpeek tool, I noticed that the big dlls contains far more namespaces and APIs compared to System.Runtime.dll under dotnet/shared on my windows machine. Why is this the case? I expected the dlls to be very close to what's officially distributed. Is it related to the fact that these 240KB dlls are under "ref" directory and targeting netcoreapp? If so, what does "ref" indicate and why aren't netfx/netstandard dlls as big as the netcoreapp dll?
Assemblies under "ref" are what is known as "Reference Assemblies". Here is a document with a description of what that means.
Reference assemblies are usually smaller than "implementation assemblies" because they do not actually contain the real implementation for each type -- just the reference signature.
In this case, the "implementation assemblies" are actually very small because they are "facade assemblies". A facade assembly contains type forwards which just point to a different assembly, which contains the real implementation. This is why they are smaller. System.Runtime is almost completely implemented in System.Private.CoreLib.dll, so most of its members are actually implemented there.
why aren't netfx/netstandard dlls as big as the netcoreapp dll?
I didn't look at anything specifically, but it's probably because .NET Core implements more functionality in the System.Runtime contract than is available in .NET Framework and .NET Standard. So the reference assembly will be slightly larger.
Thanks for the detailed explanation! However, it's hard to believe that reference assemblies are actually bigger than non-ref assemblies:
-rw-r--r-- 1 242K Sep 8 04:38 ./obj/ref/System.Runtime/4.2.0.0/netcoreapp/System.Runtime.dll
-rw-r--r-- 2 242K Sep 8 04:38 ./ref/netcoreapp/System.Runtime.dll
-rw-r--r-- 2 242K Sep 8 04:38 ./ref/System.Runtime/4.2.0.0/netcoreapp/System.Runtime.dll
Also, comparing the ref. assemblies between different frameworks:
-rw-r--r-- 2 242K Sep 8 04:38 ./ref/netcoreapp/System.Runtime.dll
-rw-r--r-- 2 29K Jul 7 06:15 ./ref/netfx/System.Runtime.dll
-rw-r--r-- 2 29K Jul 7 06:15 ./ref/netstandard/System.Runtime.dll
Granted, the timestamp and the file sizes are correlated. I'll try to see if I can repro this on a different linux machine.
Hi @sstevenkang . The smallest assemblies you see are just type forwards. (You can open them in ILSPY or a similar tool). ref/netstandard/System.Runtime.dll for example is 29KB and type forwards everything to a netstandard.dll reference assembly. That is a regular reference assembly that contains skeletons for all the types and it is ~1.3MB. In your list above those that are 242KB are regular reference assemblies, for the System.Runtime.dll surface area.
Underlying all this, not shown are the runtime assemblies. There are System.Runtime.dll runtime assemblies, but they are just type forwards to the actual implementation, which is entirely in System.Private.Corelib.dll.
The effect of all this is that you can mix and match binaries that reference String in System.Runtime.dll or netstandard.dll or in fact mscorlib.dll and references will all resolve at both compile and runtime.
Let me know if you have any other questions, good observation.
Ah it looks like @mellinoe basically said this but in different words. I suggest you try looking at them in ILSPY or Reflector and you'll see which are typeforwards and which are not. (ILSPY doesn't show where the type forward to - I use ILDASM to see that)
ILSPY doesn't show where the type forward to - I use ILDASM to see that
If you switch the decompilation mode to "IL" (instead of C# or VB), then you can actually see the type-forward destinations in ILSpy, as well.
Thanks @danmosemsft and @mellinoe. Just for my understanding, what's the reasoning behind creating mixed types of the dll? I would assume that System.Runtime.dll for netcoreapp, netfx, netstandard are all either type-forwarded assemblies or all reference assemblies.
And, to bring it back to my original question... my goal is to reproduce an official dotnet distribution from the build artifacts of each dotnet repos. However, it's quite difficult to comprehend how the build artifacts are organized--not to mention some of the build output directories contain assemblies from dependent packages fetched from nuget.org.
Today, I have a crude Makefile pulling various executables, dlls, and shared objects across build output directories of dotnet repos and putthem them together into a dotnet installation. Do you have any suggestion or guidance on how to create a private dotnet distro in a way that's more structured and repeatable over releases?
@sstevenkang the complication is netstandard.dll. This was introduced as part of .NET Standard 2.0 because we needed to pick some surface area for .NET Standard 2.0 libraries to build against. There were various reasons we didn't simply choose mscorlib based or system.runtime based and typeforward the other one. One reason was that on a platform where those typeforwards were not deployed, it is easier to deploy the single netstandard.dll with your app than a collection of separate type forward assemblies.
Can you help me understand what you are trying to achieve with your distro? For deploying an app, one would simply do dotnet publish
The purpose of all of the different types of DLL's, and different configurations (netfx, netstandard, netcoreapp, uap, etc.) is because our different platforms are implemented differently -- the reference assemblies help to paper over those differences. The multitude of reference assemblies and implementation assemblies, and their differences, are a consequence of us trying to support tons of different runtimes, and cross-targeting between them.
If you are building your own distribution of _.NET Core_ (e.g. the CoreCLR-based cross-platform runtime), then you only need to be concerned with the .NET Core binaries. That is the "netcoreapp" build configuration, and also the default one if you just type "build.cmd/sh".
Thank you! I think I got all the information I needed.
Most helpful comment
If you switch the decompilation mode to "IL" (instead of C# or VB), then you can actually see the type-forward destinations in ILSpy, as well.