3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
10 // <OWNER>Microsoft</OWNER>
12 // Infrastructure for setting up concurrent work, marshaling exceptions, determining
13 // the recommended degree-of-parallelism, and so forth.
15 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
18 using System.Collections.Generic;
19 using System.Globalization;
20 using System.Runtime.InteropServices;
21 using System.Threading;
22 using System.Diagnostics.Contracts;
23 using System.Threading.Tasks;
24 using System.Security;
25 using System.Security.Permissions;
27 namespace System.Linq.Parallel
30 //-----------------------------------------------------------------------------------
31 // A simple helper class that offers common task scheduling functionality.
34 internal static class Scheduling
36 // Whether to preserve order by default, when neither AsOrdered nor AsUnordered is used.
37 internal const bool DefaultPreserveOrder = false;
39 // The default degree of parallelism, or -1 if unspecified. Dev unit tests set this value
40 // to change the default DOP.
41 internal static int DefaultDegreeOfParallelism = Math.Min(Environment.ProcessorCount, MAX_SUPPORTED_DOP);
43 // The size to use for bounded buffers.
44 internal const int DEFAULT_BOUNDED_BUFFER_CAPACITY = 512;
46 // The number of bytes we want "chunks" to be, when partitioning, etc. We choose 4 cache
47 // lines worth, assuming 128b cache line. Most (popular) architectures use 64b cache lines,
48 // but choosing 128b works for 64b too whereas a multiple of 64b isn't necessarily sufficient
49 // for 128b cache systems. So 128b it is.
50 internal const int DEFAULT_BYTES_PER_CHUNK = 128 * 4;
52 // The number of milliseconds before we assume a producer has been zombied.
53 internal const int ZOMBIED_PRODUCER_TIMEOUT = Timeout.Infinite;
55 // The largest number of partitions that PLINQ supports.
56 internal const int MAX_SUPPORTED_DOP = 512;
59 //-----------------------------------------------------------------------------------
60 // Calculates the proper amount of DOP. This takes into consideration dynamic nesting.
63 internal static int GetDefaultDegreeOfParallelism()
65 return DefaultDegreeOfParallelism;
68 //-----------------------------------------------------------------------------------
69 // Gets the recommended "chunk size" for a particular CLR type.
72 // We try to recommend some reasonable "chunk size" for the data, but this is
73 // clearly a tradeoff, and requires a bit of experimentation. A larger chunk size
74 // can help locality, but if it's too big we may end up either stalling another
75 // partition (if enumerators are calculating data on demand) or skewing the
76 // distribution of data among the partitions.
79 internal static int GetDefaultChunkSize<T>()
83 if (typeof(T).IsValueType)
87 #if !SILVERLIGHT //StructLayoutAttribute is not supported in CoreCLR/CoreSys
88 if (typeof(T).StructLayoutAttribute.Value == LayoutKind.Explicit)
90 chunkSize = Math.Max(1, DEFAULT_BYTES_PER_CHUNK / Marshal.SizeOf(typeof(T)));
95 // We choose '128' because this ensures, no matter the actual size of the value type,
96 // the total bytes used will be a multiple of 128. This ensures it's cache aligned.
102 Contract.Assert((DEFAULT_BYTES_PER_CHUNK % IntPtr.Size) == 0, "bytes per chunk should be a multiple of pointer size");
103 chunkSize = (DEFAULT_BYTES_PER_CHUNK / IntPtr.Size);
106 TraceHelpers.TraceInfo("Scheduling::GetDefaultChunkSize({0}) -- returning {1}", typeof(T), chunkSize);