const int bitcount = sizeof(long) * 8; //longのbit数 /// <summary>複合ソート法のマスク1個分をやる</summary> /// <param name="Index">nCxの組合せの何個目か(0~(nCx)-1)</param> async Task <Task> MultipleSortUnit(int Index) { int[] BaseBlocks = Combi[Index]; int StartBlock = BaseBlocks.Last(); long SortMask = UnMask(BaseBlocks, Combi.Choice); var QuickSortSW = Stopwatch.StartNew(); //適当なサイズに切ってそれぞれをクイックソート int SortedFileCount = await SplitQuickSort.QuickSortAll(Index, SortMask).ConfigureAwait(false); //↓ベンチマーク用 sortのファイル数を入れてやる //int SortedFileCount = 52; GC.Collect(GC.MaxGeneration, GCCollectionMode.Default, true, false); GC.WaitForPendingFinalizers(); GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); QuickSortSW.Stop(); Console.WriteLine("{0}\tFile Sort\t{1}ms ", Index, QuickSortSW.ElapsedMilliseconds); long[] UnMasks = Enumerable.Range(0, Combi.Choice).Select(i => UnMask(i, Combi.Choice)).ToArray(); var MultipleSortBlock = new ActionBlock <AddOnlyList <long> >((BlockList) => { var Block = BlockList.InnerArray; var NewHash = this.NewHash; int LocalPairCount = 0; //見つけた組合せの数を数える int BlockIndex = 0; int CurrentBlockLength; //(要素数,実際の要素,…),0 という配列を読み込んでいく for (; (CurrentBlockLength = (int)Block[BlockIndex]) > 0; BlockIndex += CurrentBlockLength + 1) { //「実際の要素」1セット分を取り出す var SortedSpan = Block.AsSpan(BlockIndex + 1, CurrentBlockLength); //新しい値を含まないやつは省略 if (NewHash != null) { for (int i = 0; i < SortedSpan.Length; i++) { if (NewHash.Contains(SortedSpan[i])) { goto HasNewHash; } } continue; } HasNewHash: for (int i = 0; i < SortedSpan.Length; i++) { long Sorted_i = SortedSpan[i]; bool NeedInsert_i = NewHash?.Contains(Sorted_i) ?? true; //long maskedhash_i = Sorted_i & SortMask; for (int j = i + 1; j < SortedSpan.Length; j++) { long Sorted_j = SortedSpan[j]; //重複排除を行う(同じハッシュ同士を比較しないだけ) if (Sorted_i == Sorted_j) { continue; } //if (maskedhash_i != (Sorted[j] & FullMask)) { break; } //これはSortedFileReaderがやってくれる //間違いなくすでにDBに入っているペアは処理しない if ((NeedInsert_i || NewHash.Contains(Sorted_j)) //NewHashがnullなら後者は処理されないからセーフ //ブロックソートで一致した組のハミング距離を測る && BitOperations.PopCount((ulong)(Sorted_i ^ Sorted_j)) <= MaxHammingDistance) { //一致したペアが見つかる最初の組合せを調べる int matchblockindex = 0; int x; for (x = 0; x < UnMasks.Length && x < StartBlock && matchblockindex < BaseBlocks.Length; x++) { if (BaseBlocks.Contains(x)) { if (x < BaseBlocks[matchblockindex]) { break; } matchblockindex++; } else { if ((Sorted_i & UnMasks[x]) == (Sorted_j & UnMasks[x])) { if (x < BaseBlocks[matchblockindex]) { break; } matchblockindex++; } } } //最初の組合せだったときだけ入れる if (x == StartBlock) { LocalPairCount++; PairBatchBlock.Post(new HashPair(Sorted_i, Sorted_j)); } } } } } BlockList.Dispose(); Interlocked.Add(ref PairCount, LocalPairCount); }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount, BoundedCapacity = Environment.ProcessorCount << 1, SingleProducerConstrained = true }); var MultipleSortSW = Stopwatch.StartNew(); //クイックソートしたファイル群をマージソートしながら読み込む using (var Reader = new MergeSortReader(Index, SortedFileCount, SortMask)) { int PostponeCount = 1 + config.hash.MergeSortPostponePairCount / DBHandler.StoreMediaPairsUnit; var GCStopWatch = Stopwatch.StartNew(); AddOnlyList <long> Sorted; while ((Sorted = Reader.ReadBlocks()) != null) { await MultipleSortBlock.SendAsync(Sorted).ConfigureAwait(false); //DBにハッシュのペアを入れる処理が詰まったら休んだりGCしたりする while (PostponeCount < PairStoreBlock.InputCount) { if (60000 <= GCStopWatch.ElapsedMilliseconds) { GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); GCStopWatch.Restart(); } await Task.Delay(1000).ConfigureAwait(false); } } } //余りをDBに入れるついでにここで制御を戻してしまう MultipleSortBlock.Complete(); return(MultipleSortBlock.Completion .ContinueWith((_) => { MultipleSortSW.Stop(); Console.WriteLine("{0}\tMerge+Comp\t{1}ms", Index, MultipleSortSW.ElapsedMilliseconds); })); }