306b5411b33c943a72caf0addab20797d081046b
[mono.git] / mcs / class / referencesource / System.Core / System / Linq / Parallel / Scheduling / Scheduling.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
7 //
8 // Scheduling.cs
9 //
10 // <OWNER>Microsoft</OWNER>
11 //
12 // Infrastructure for setting up concurrent work, marshaling exceptions, determining
13 // the recommended degree-of-parallelism, and so forth.
14 //
15 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
16
17 using System;
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;
26
27 namespace System.Linq.Parallel
28 {
29
30     //-----------------------------------------------------------------------------------
31     // A simple helper class that offers common task scheduling functionality.
32     //
33
34     internal static class Scheduling
35     {
36         // Whether to preserve order by default, when neither AsOrdered nor AsUnordered is used.
37         internal const bool DefaultPreserveOrder = false;
38
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);
42
43         // The size to use for bounded buffers. 
44         internal const int DEFAULT_BOUNDED_BUFFER_CAPACITY = 512;
45
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; 
51
52         // The number of milliseconds before we assume a producer has been zombied.
53         internal const int ZOMBIED_PRODUCER_TIMEOUT = Timeout.Infinite;
54
55         // The largest number of partitions that PLINQ supports.
56         internal const int MAX_SUPPORTED_DOP = 512;
57
58
59         //-----------------------------------------------------------------------------------
60         // Calculates the proper amount of DOP.  This takes into consideration dynamic nesting.
61         //
62
63         internal static int GetDefaultDegreeOfParallelism()
64         {
65             return DefaultDegreeOfParallelism;
66         }
67
68         //-----------------------------------------------------------------------------------
69         // Gets the recommended "chunk size" for a particular CLR type.
70         //
71         // Notes:
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.
77         //
78
79         internal static int GetDefaultChunkSize<T>()
80         {
81             int chunkSize;
82
83             if (typeof(T).IsValueType)
84             {
85                 // @
86
87 #if !SILVERLIGHT //StructLayoutAttribute is not supported in CoreCLR/CoreSys
88                 if (typeof(T).StructLayoutAttribute.Value == LayoutKind.Explicit)
89                 {
90                     chunkSize = Math.Max(1, DEFAULT_BYTES_PER_CHUNK / Marshal.SizeOf(typeof(T)));
91                 }
92                 else
93 #endif
94                 {
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.
97                     chunkSize = 128;
98                 }
99             }
100             else
101             {
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);
104             }
105
106             TraceHelpers.TraceInfo("Scheduling::GetDefaultChunkSize({0}) -- returning {1}", typeof(T), chunkSize);
107
108             return chunkSize;
109         }
110
111     }
112
113 }