using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Threading; using System.Linq; using glue; using glue.Debugging; using glue.Extensions.Enumerable; using System.Threading.Tasks; using glue.Tasks; #pragma warning disable 0649 namespace agree { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sealed public class ConcurrentTray : Tray, IFreezableTray { public ConcurrentTray(int tix, TypeMgr tm, int last_mark, int next_id) : base(tix, tm, last_mark, next_id) { for (int i = 0; i < Pools.Length; i++) Pools[i] = new Pool(this, i, 223, 525713); } public void Freeze() { //foreach (Pool p in Pools) //{ // p.Resize(-1); //} } public void Shrink() { //int b4 = Pools.Sum(cp => ((Pool)cp).m_buckets.Length); foreach (Pool p in Pools) p.Shrink(); //Console.WriteLine("{0} -> {1}", b4, Pools.Sum(cp => ((Pool)cp).m_buckets.Length)); } public int c_resizes = 0; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Report() { Console.WriteLine(); Console.WriteLine("fix feature PMs edges m-min m-max in-stdev density types nzm buckets"); int o_min = int.MaxValue; int o_max = int.MinValue; int c_pm = 0; HashSet<int> in_use = new HashSet<int>(); foreach (Pool p in Pools) { int n = 0; double mx = 0, mean = 0; int min = int.MaxValue; int max = int.MinValue; HashSet<Edge> d_edges = new HashSet<Edge>(); HashSet<Type> d_types = new HashSet<Type>(); int c_nonzero = 0; foreach (var pe in p.PoolEdges) { int m = pe.Key; n++; double delta = m - mean; mean += delta / n; mx += delta * (m - mean); if (m < min) min = m; if (m > max) max = m; in_use.Add(m); d_edges.Add(pe.Value); d_types.Add(tm.GetEdgeType(pe.Value.FlagsId)); if (pe.Value.Mark != 0) c_nonzero++; } c_pm += n; if (min < o_min) o_min = min; if (max > o_max) o_max = max; String extra = ""; //if (d_edges.Count <= 8) // extra = d_edges.Select(e => e.ToString()).StringJoin(" "); double variance = n <= 1 ? Double.NaN : mx / (n - 1); Console.WriteLine("{0,3} {1,16} {2,6} {3,6} {4,6} {5,7} {6,9:0.0} {7,10:0.000} {8,5} {9,5} {10,7} {11}", p.i_feat, p.Feature.ToUpper(), n, d_edges.Count, min, max, Math.Sqrt(variance), (n * 100.0) / (max - min), d_types.Count, c_nonzero > 0 ? c_nonzero.ToString() : "", #if DEBUG p.m_buckets.Length, #else "n/a", #endif extra); } Console.WriteLine(); Console.WriteLine("overall mark usage:"); Console.WriteLine("PMs: {0} distinct: {1} m-min: {2} m-max: {3} range-unused: {4} density: {5:0.000}", c_pm, in_use.Count, o_min, o_max, (o_max - o_min + 1) - in_use.Count, (in_use.Count * 100.0) / (o_max - o_min)); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sealed public class Pool : ConstraintPool { sealed internal class Node { internal Edge m_edge; readonly internal int m_mark; internal Node m_next; internal Node(Edge e, int m, Node next) { m_edge = e; m_mark = m; m_next = next; } }; private const int DEFAULT_CAPACITY = 31; private const int DEFAULT_CONCURRENCY_MULTIPLIER = 4; FalseSharing.Padding56 fsp1; //#if DEBUG internal //#endif volatile Node[] m_buckets; FalseSharing.Padding56 fsp2; int[] m_countPerLock; FalseSharing.Padding56 fsp3; readonly Object[] m_locks; FalseSharing.Padding56 fsp4; public Pool(Tray tr, int i_feat) : this(tr, i_feat, DEFAULT_CONCURRENCY_MULTIPLIER * Environment.ProcessorCount, DEFAULT_CAPACITY, null) { } public Pool(Tray tr, int i_feat, IEnumerable<KeyValuePair<int, Edge>> collection) : this(tr, i_feat, DEFAULT_CONCURRENCY_MULTIPLIER * Environment.ProcessorCount, DEFAULT_CAPACITY, collection) { } public Pool(Tray tr, int i_feat, int concurrencyLevel, IEnumerable<KeyValuePair<int, Edge>> collection) : this(tr, i_feat, concurrencyLevel, DEFAULT_CAPACITY, collection) { } public Pool(Tray tr, int i_feat, int concurrencyLevel, int capacity) : this(tr, i_feat, concurrencyLevel, capacity, null) { } public Pool(Tray tr, int i_feat, int concurrencyLevel, int capacity, IEnumerable<KeyValuePair<int, Edge>> collection) : base(tr, i_feat) { Nop.X(fsp1, fsp2, fsp3, fsp4); if (concurrencyLevel < 1) throw new ArgumentOutOfRangeException("concurrencyLevel"); if (capacity < 0) throw new ArgumentOutOfRangeException("capacity"); // The capacity should be at least as large as the concurrency level. Otherwise, we would have locks that don't guard // any buckets. if (capacity < concurrencyLevel) capacity = concurrencyLevel; m_locks = new Object[concurrencyLevel]; for (int i = 0; i < m_locks.Length; i++) m_locks[i] = new Object(); m_countPerLock = new int[m_locks.Length]; m_buckets = new Node[capacity]; if (collection != null) { foreach (var pair in collection) { int bucketNo = pair.Key % m_buckets.Length; int lockNo = bucketNo % m_locks.Length; Node first; for (Node cur = first = m_buckets[bucketNo]; cur != null; cur = cur.m_next) if (cur.m_mark == pair.Key) throw new ArgumentException(); // The key was not found in the bucket. Insert the key-value pair. m_buckets[bucketNo] = new Node(pair.Value, pair.Key, first); m_countPerLock[lockNo]++; // If this lock has element / bucket ratio greater than 1, resize the entire table. // Note: the formula is chosen to avoid overflow, but has a small inaccuracy due to // rounding. if (m_countPerLock[lockNo] > m_buckets.Length / m_locks.Length) Resize(); } } } public override bool ContainsInMark(int m) { Debug.Assert(m != 0); Node[] buckets = m_buckets; Node n = buckets[m % buckets.Length]; while (n != null) { if (n.m_mark == m) return true; n = n.m_next; } return false; } public override Edge GetEdge(Int32 m) { Debug.Assert(m != 0); Node[] buckets = m_buckets; Node n = buckets[m % buckets.Length]; for (; n != null; n = n.m_next) if (n.m_mark == m) return n.m_edge; return default(Edge); } public override bool TryGetEdge(int m, out Edge e) { Debug.Assert(m != 0); Node[] buckets = m_buckets; Node n = buckets[m % buckets.Length]; for (; n != null; n = n.m_next) if (n.m_mark == m) { e = n.m_edge; return true; } e = default(Edge); return false; } public unsafe override void SetEdge(int m, Edge e) { Debug.Assert(m != 0 && m >= tr._protect_mark && e.FlagsId != 0); int i_bucket, i_lock, c; Object o; Node[] buckets; while (true) { buckets = m_buckets; o = m_locks[i_lock = (i_bucket = m % (c = buckets.Length)) % m_locks.Length]; Monitor.Enter(o); if (buckets == m_buckets) break; Monitor.Exit(o); } Node first; for (Node cur = first = buckets[i_bucket]; cur != null; cur = cur.m_next) { if (cur.m_mark == m) { fixed (Edge* pe = &cur.m_edge) *(long*)pe = *(long*)&e; Monitor.Exit(o); return; } } buckets[i_bucket] = new Node(e, m, first); m_countPerLock[i_lock]++; bool resizeDesired = m_countPerLock[i_lock] > c / m_locks.Length; Monitor.Exit(o); if (resizeDesired) Resize(); return; } public override void RemoveEdge(int m) { Debug.Assert(m != 0 && m >= tr._protect_mark); int i_bucket, i_lock; Object o; Node[] buckets; while (true) { buckets = m_buckets; o = m_locks[i_lock = (i_bucket = m % buckets.Length) % m_locks.Length]; Monitor.Enter(o); if (buckets == m_buckets) break; Monitor.Exit(o); } for (Node prev = null, cur = buckets[i_bucket]; cur != null; prev = cur, cur = cur.m_next) { if (cur.m_mark == m) { if (prev == null) buckets[i_bucket] = cur.m_next; else prev.m_next = cur.m_next; m_countPerLock[i_lock]--; break; } } Monitor.Exit(o); } public override bool TryRemoveEdge(int m, out Edge e) { Debug.Assert(m != 0 && m >= tr._protect_mark); int i_bucket, i_lock; Object o; Node[] buckets; while (true) { buckets = m_buckets; o = m_locks[i_lock = (i_bucket = m % buckets.Length) % m_locks.Length]; Monitor.Enter(o); if (buckets == m_buckets) break; Monitor.Exit(o); } for (Node prev = null, cur = buckets[i_bucket]; cur != null; prev = cur, cur = cur.m_next) { if (cur.m_mark == m) { if (prev == null) buckets[i_bucket] = cur.m_next; else prev.m_next = cur.m_next; e = cur.m_edge; m_countPerLock[i_lock]--; Monitor.Exit(o); return true; } } Monitor.Exit(o); e = default(Edge); return false; } public override int Count { get { int count = 0; try { // Acquire all locks EnterAllLocks(); // Compute the count, we allow overflow for (int i = 0; i < m_countPerLock.Length; i++) count += m_countPerLock[i]; } finally { // Release locks that have been acquired earlier ExitAllLocks(); } return count; } } public override IEnumerable<int> Marks { get { tr.mre_truncate_ok.WaitOne(); #if LOCKED_ENUMS int acquiredLocks = 0; try { AcquireLocks(0, m_locks.Length, ref acquiredLocks); Debug.Assert(acquiredLocks == m_locks.Length); List<int> keys = new List<int>(); for (int i = 0; i < m_buckets.Length; i++) { for (Node current = m_buckets[i]; current != null; current = current.m_next) keys.Add(current.m_mark); } return new ReadOnlyCollection<int>(keys); } finally { ReleaseLocks(0, acquiredLocks); } #else foreach (Node n in m_buckets) for (Node cur = n; cur != null; cur = cur.m_next) yield return cur.m_mark; #endif } } public override IEnumerable<Edge> Edges { get { tr.mre_truncate_ok.WaitOne(); #if LOCKED_ENUMS int acquiredLocks = 0; try { AcquireLocks(0, m_locks.Length, ref acquiredLocks); Debug.Assert(acquiredLocks == m_locks.Length); List<Edge> values = new List<Edge>(); for (int i = 0; i < m_buckets.Length; i++) { for (Node current = m_buckets[i]; current != null; current = current.m_next) values.Add(current.m_edge); } return new ReadOnlyCollection<Edge>(values); } finally { ReleaseLocks(0, acquiredLocks); } #else foreach (Node n in m_buckets) for (Node cur = n; cur != null; cur = cur.m_next) yield return cur.m_edge; #endif } } public override IEnumerable<KeyValuePair<int, Edge>> PoolEdges { get { tr.mre_truncate_ok.WaitOne(); #if LOCKED_ENUMS Node[] buckets = m_buckets; for (int i = 0; i < buckets.Length; i++) { Node current = buckets[i]; // The memory barrier ensures that the load of the fields of 'current' doesn’t move before the load from buckets[i]. Thread.MemoryBarrier(); while (current != null) { yield return new KeyValuePair<int, Edge>(current.m_mark, current.m_edge); current = current.m_next; } } #else foreach (Node n in m_buckets) for (Node cur = n; cur != null; cur = cur.m_next) yield return new KeyValuePair<int, Edge>(cur.m_mark, cur.m_edge); #endif } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// extra /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Clear() { try { EnterAllLocks(); Thread.MemoryBarrier(); m_buckets = new Node[DEFAULT_CAPACITY]; Array.Clear(m_countPerLock, 0, m_countPerLock.Length); } finally { ExitAllLocks(); } } public bool IsEmpty { get { try { EnterAllLocks(); for (int i = 0; i < m_countPerLock.Length; i++) { if (m_countPerLock[i] != 0) return false; } } finally { ExitAllLocks(); } return true; } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// internal /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void EnterAllLocks() { for (int i = 0; i < m_locks.Length; i++) Monitor.Enter(m_locks[i]); } void ExitAllLocks() { for (int i = 0; i < m_locks.Length; i++) Monitor.Exit(m_locks[i]); } public void Resize() { //if (tr.c_truncators > 0) //{ // //foreach (Thread th in tr.truncators) // // th.Priority = ThreadPriority.AboveNormal; // return; //} Node[] buckets = m_buckets; Monitor.Enter(m_locks[0]); if (buckets != m_buckets) { Monitor.Exit(m_locks[0]); return; } using (var q = new ThreadPriorityRegion(ThreadPriority.Highest)) { int c = (int)(buckets.Length * 2) - 5; while (c % 3 == 0 || c % 5 == 0 || c % 7 == 0 || c % 11 == 0) c -= 2; Node[] newBuckets = new Node[c]; int[] newCountPerLock = new int[m_locks.Length]; for (int i = 1; i < m_locks.Length; i++) Monitor.Enter(m_locks[i]); // Copy all data into a new table, creating new nodes for all elements for (int i = 0; i < buckets.Length; i++) { for (Node current = buckets[i]; current != null; current = current.m_next) { int newBucketNo = current.m_mark % newBuckets.Length; int newLockNo = newBucketNo % m_locks.Length; newBuckets[newBucketNo] = new Node(current.m_edge, current.m_mark, newBuckets[newBucketNo]); newCountPerLock[newLockNo]++; } } Interlocked.Increment(ref ((ConcurrentTray)tr).c_resizes); Thread.MemoryBarrier(); m_buckets = newBuckets; m_countPerLock = newCountPerLock; for (int i = 0; i < m_locks.Length; i++) Monitor.Exit(m_locks[i]); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// internal /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Shrink() { //tr.mre_truncate_ok.Wait(); Node[] buckets = m_buckets; int c = m_countPerLock.Sum(); if (c == buckets.Length) return; Node[] newBuckets = new Node[c]; int[] newCountPerLock = new int[m_locks.Length]; // Copy all data into a new table, creating new nodes for all elements for (int i = 0; i < buckets.Length; i++) { Node next; for (Node current = buckets[i]; current != null; current = next) { next = current.m_next; int newBucketNo = current.m_mark % newBuckets.Length; int newLockNo = newBucketNo % m_locks.Length; current.m_next = newBuckets[newBucketNo]; newBuckets[newBucketNo] = current; newCountPerLock[newLockNo]++; } } // And finally adjust m_buckets and m_countPerLock to point to data for the new table Thread.MemoryBarrier(); m_buckets = newBuckets; m_countPerLock = newCountPerLock; } }; }; }