© 2013, NVIDIA CORPORATION. All rights reserved.
Code and text by Sean Baxter, NVIDIA Research.
(Click here for license. Click here for contact information.)
Segmented sort and locality sort are high-performance variants of mergesort that operate on non-uniform random data. Segmented sort allows us to sort many variable-length arrays in parallel. A list of head indices provided to define segment intervals. Segmented sort is fast: not only is segmentation supported for negligible cost, the function takes advantage of early-exit opportunities to improve throughput over vanilla mergesort. Locality sort detects regions of approximate sortedness without requiring annotations.
Segmented sort keys benchmark from benchmarksegsort/benchmarksegsort.cu
Segmented sort keys demonstration from tests/demo.cu
void DemoSegSortKeys(CudaContext& context) { printf("\n\nSEG-SORT KEYS DEMONSTRATION:\n\n"); // Use CudaContext::GenRandom to generate 100 random integers between 0 and // 9. int N = 100; MGPU_MEM(int) keys = context.GenRandom<int>(N, 0, 99); // Define 10 segment heads (for 11 segments in all). const int NumSegs = 10; const int SegHeads[NumSegs] = { 4, 19, 22, 56, 61, 78, 81, 84, 94, 97 }; MGPU_MEM(int) segments = context.Malloc(SegHeads, 10); printf("Input keys:\n"); PrintArray(*keys, "%4d", 10); printf("\nSegment heads:\n"); PrintArray(*segments, "%4d", 10); // Sort within segments. SegSortKeysFromIndices(keys->get(), N, segments->get(), NumSegs, context); printf("\nSorted data (segment heads are marked by *):\n"); PrintArrayOp(*keys, FormatOpMarkArray(" %c%2d", SegHeads, NumSegs), 10); }
SEG-SORT KEYS DEMONSTRATION: Input keys: 0: 42 39 9 77 59 97 47 74 69 63 10: 69 7 63 63 3 52 6 29 31 32 20: 53 63 65 99 40 51 81 72 71 24 30: 96 33 53 74 32 68 10 68 61 7 40: 77 45 42 69 9 6 26 6 15 52 50: 28 26 44 48 52 13 45 9 87 12 60: 51 96 94 75 63 26 95 72 24 41 70: 67 47 28 5 67 61 69 49 6 90 80: 25 93 22 91 66 30 84 79 34 22 90: 78 44 67 51 0 23 60 71 38 98 Segment heads: 0: 4 19 22 56 61 78 81 84 94 97 Sorted data (segment heads are marked by *): 0: 9 39 42 77 * 3 6 7 29 31 47 10: 52 59 63 63 63 69 69 74 97 *32 20: 53 63 * 6 6 7 9 10 13 15 24 30: 26 26 28 32 33 40 42 44 45 48 40: 51 52 52 53 61 65 68 68 69 71 50: 72 74 77 81 96 99 * 9 12 45 51 60: 87 * 5 24 26 28 41 47 49 61 63 70: 67 67 69 72 75 94 95 96 * 6 25 80: 90 *22 91 93 *22 30 34 44 51 66 90: 67 78 79 84 * 0 23 60 *38 71 98
Segmented sort pairs benchmark from benchmarksegsort/benchmarksegsort.cu
Segmented sort pairs demonstration from tests/demo.cu
void DemoSegSortPairs(CudaContext& context) { printf("\n\nSEG-SORT PAIRS DEMONSTRATION:\n\n"); // Use CudaContext::GenRandom to generate 100 random integers between 0 and // 9. int N = 100; MGPU_MEM(int) keys = context.GenRandom<int>(N, 0, 99); // Fill values with ascending integers. MGPU_MEM(int) values = context.FillAscending<int>(N, 0, 1); // Define 10 segment heads (for 11 segments in all). const int NumSegs = 10; const int SegHeads[NumSegs] = { 4, 19, 22, 56, 61, 78, 81, 84, 94, 97 }; MGPU_MEM(int) segments = context.Malloc(SegHeads, 10); printf("Input keys:\n"); PrintArray(*keys, "%4d", 10); printf("\nSegment heads:\n"); PrintArray(*segments, "%4d", 10); // Sort within segments. SegSortPairsFromIndices(keys->get(), values->get(), N, segments->get(), NumSegs, context); printf("\nSorted data (segment heads are marked by *):\n"); PrintArrayOp(*keys, FormatOpMarkArray(" %c%2d", SegHeads, NumSegs), 10); printf("\nSorted indices (segment heads are marked by *):\n"); PrintArrayOp(*values, FormatOpMarkArray(" %c%2d", SegHeads, NumSegs), 10); }
SEG-SORT PAIRS DEMONSTRATION: Input keys: 0: 91 65 0 27 46 46 42 0 46 44 10: 77 97 32 30 78 21 47 24 3 80 20: 17 48 72 40 47 21 15 54 34 72 30: 60 28 19 54 73 75 24 33 91 80 40: 26 85 76 1 18 88 28 59 9 8 50: 57 92 68 91 54 98 42 90 64 94 60: 64 93 67 0 63 77 94 2 20 58 70: 70 64 23 32 11 11 60 12 45 97 80: 45 53 66 66 77 70 35 6 66 20 90: 41 43 84 1 83 6 25 34 61 31 Segment heads: 0: 4 19 22 56 61 78 81 84 94 97 Sorted data (segment heads are marked by *): 0: 0 27 65 91 * 0 3 21 24 30 32 10: 42 44 46 46 46 47 77 78 97 *17 20: 48 80 * 1 8 9 15 18 19 21 24 30: 26 28 28 33 34 40 47 54 54 54 40: 57 59 60 68 72 72 73 75 76 80 50: 85 88 91 91 92 98 *42 64 64 90 60: 94 * 0 2 11 11 12 20 23 32 58 70: 60 63 64 67 70 77 93 94 *45 45 80: 97 *53 66 66 * 1 6 20 35 41 43 90: 66 70 77 84 * 6 25 83 *31 34 61 Sorted indices (segment heads are marked by *): 0: 2 3 1 0 * 7 18 15 17 13 12 10: 6 9 4 5 8 16 10 14 11 *20 20: 21 19 *43 49 48 26 44 32 25 36 30: 40 31 46 37 28 23 24 27 33 54 40: 50 47 30 52 22 29 34 35 42 39 50: 41 45 38 53 51 55 *56 58 60 57 60: 59 *63 67 74 75 77 68 72 73 69 70: 76 64 71 62 70 65 61 66 *78 80 80: 79 *81 82 83 *93 87 89 86 90 91 90: 88 85 84 92 *95 96 94 *99 97 98
Locality sort keys benchmark from benchmarklocalitysort/benchmarklocalitysort.cu
Locality sort keys demonstration from tests/demo.cu
void DemoLocalitySortKeys(CudaContext& context) { printf("\n\nLOCALITY SORT KEYS DEMONSTRATION:\n\n"); // Generate keys that are roughly sorted but with added noise. int N = 100; std::vector<int> keysHost(N); for(int i = 0; i < N; ++i) keysHost[i] = i + Rand(0, 25); MGPU_MEM(int) keys = context.Malloc(keysHost); printf("Input keys:\n"); PrintArray(*keys, "%4d", 10); // Sort by exploiting locality. LocalitySortKeys(keys->get(), N, context); printf("\nSorted data:\n"); PrintArray(*keys, "%4d", 10);; }
LOCALITY SORT KEYS DEMONSTRATION: Input keys: 0: 15 26 16 9 26 27 12 16 16 28 10: 13 32 36 18 30 40 28 35 34 44 20: 34 40 38 28 38 34 44 32 41 50 30: 55 55 37 52 36 57 38 48 39 47 40: 50 62 53 57 53 48 65 52 64 61 50: 70 61 76 72 79 64 60 77 61 84 60: 78 83 64 84 77 74 79 68 90 94 70: 82 92 82 95 91 76 95 77 91 94 80: 89 100 85 99 99 102 92 111 89 95 90: 109 114 98 96 105 103 113 119 107 105 Sorted data: 0: 9 12 13 15 16 16 16 18 26 26 10: 27 28 28 28 30 32 32 34 34 34 20: 35 36 36 37 38 38 38 39 40 40 30: 41 44 44 47 48 48 50 50 52 52 40: 53 53 55 55 57 57 60 61 61 61 50: 62 64 64 64 65 68 70 72 74 76 60: 76 77 77 77 78 79 79 82 82 83 70: 84 84 85 89 89 90 91 91 92 92 80: 94 94 95 95 95 96 98 99 99 100 90: 102 103 105 105 107 109 111 113 114 119
Locality sort pairs benchmark from benchmarklocalitysort/benchmarklocalitysort.cu
Locality sort pairs demonstration from tests/demo.cu
void DemoLocalitySortPairs(CudaContext& context) { printf("\n\nLOCALITY SORT PAIRS DEMONSTRATION:\n\n"); // Generate keys that are roughly sorted but with added noise. int N = 100; std::vector<int> keysHost(N); for(int i = 0; i < N; ++i) keysHost[i] = i + Rand(0, 25); MGPU_MEM(int) keys = context.Malloc(keysHost); MGPU_MEM(int) values = context.FillAscending<int>(N, 0, 1); printf("Input keys:\n"); PrintArray(*keys, "%4d", 10); // Sort by exploiting locality. LocalitySortPairs(keys->get(), values->get(), N, context); printf("\nSorted data:\n"); PrintArray(*keys, "%4d", 10); printf("\nSorted indices:\n"); PrintArray(*values, "%4d", 10); }
LOCALITY SORT PAIRS DEMONSTRATION: Input keys: 0: 19 22 12 17 21 29 24 20 19 26 10: 10 14 20 38 25 31 23 21 23 20 20: 41 33 33 43 47 37 36 49 47 45 30: 40 54 53 33 53 53 45 52 43 41 40: 60 66 66 48 52 53 63 64 59 73 50: 71 56 71 77 58 77 78 68 83 71 60: 73 75 84 84 79 68 70 83 73 94 70: 80 87 91 84 95 75 96 79 86 92 80: 93 101 84 102 86 89 89 93 105 100 90: 102 102 96 110 106 99 99 101 99 101 Sorted data: 0: 10 12 14 17 19 19 20 20 20 21 10: 21 22 23 23 24 25 26 29 31 33 20: 33 33 36 37 38 40 41 41 43 43 30: 45 45 47 47 48 49 52 52 53 53 40: 53 53 54 56 58 59 60 63 64 66 50: 66 68 68 70 71 71 71 73 73 73 60: 75 75 77 77 78 79 79 80 83 83 70: 84 84 84 84 86 86 87 89 89 91 80: 92 93 93 94 95 96 96 99 99 99 90: 100 101 101 101 102 102 102 105 106 110 Sorted indices: 0: 10 2 11 3 0 8 7 12 19 4 10: 17 1 16 18 6 14 9 5 15 21 20: 22 33 26 25 13 30 20 39 23 38 30: 29 36 24 28 43 27 37 44 32 34 40: 35 45 31 51 54 48 40 46 47 41 50: 42 57 65 66 50 52 59 49 60 68 60: 61 75 53 55 56 64 77 70 58 67 70: 62 63 73 82 78 84 71 85 86 72 80: 79 80 87 69 74 76 92 95 96 98 90: 89 81 97 99 83 90 91 88 94 93
//////////////////////////////////////////////////////////////////////////////// // kernels/segmentedsort.cuh // Mergesort count items in-place in data_global. Keys are compared with Comp // (as they are in MergesortKeys), however keys remain inside the segments // defined by flags_global. // flags_global is a bitfield cast to uint*. Each bit in flags_global is a // segment head flag. Only keys between segment head flags (inclusive on the // left and exclusive on the right) may be exchanged. The first element is // assumed to start a segment, regardless of the value of bit 0. // Passing verbose=true causes the function to print mergepass statistics to the // console. This may be helpful for developers to understand the performance // characteristics of the function and how effectively it early-exits merge // operations. template<typename T, typename Comp> MGPU_HOST void SegSortKeysFromFlags(T* data_global, int count, const uint* flags_global, CudaContext& context, Comp comp, bool verbose = false); // SegSortKeysFromFlags specialized with Comp = mgpu::less<T>. template<typename T> MGPU_HOST void SegSortKeysFromFlags(T* data_global, int count, const uint* flags_global, CudaContext& context, bool verbose = false); // Segmented sort using head flags and supporting value exchange. template<bool Stable, typename KeyType, typename ValType, typename Comp> MGPU_HOST void SegSortPairsFromFlags(KeyType* keys_global, ValType* values_global, const uint* flags_global, int count, CudaContext& context, Comp comp, bool verbose = false); // SegSortPairsFromFlags specialized with Comp = mgpu::less<T>. template<bool Stable, typename KeyType, typename ValType> MGPU_HOST void SegSortPairsFromFlags(KeyType* keys_global, ValType* values_global, const uint* flags_global, int count, CudaContext& context, bool verbose = false) // Segmented sort using segment indices rather than head flags. indices_global // is a sorted and unique list of indicesCount segment start locations. These // indices correspond to the set bits in the flags_global field. A segment // head index for position 0 may be omitted. template<typename T, typename Comp> MGPU_HOST void SegSortKeysFromIndices(T* data_global, int count, const int* indices_global, int indicesCount, CudaContext& context, Comp comp, bool verbose = false); // SegSortKeysFromIndices specialized with Comp = mgpu::less<T>. template<typename T> MGPU_HOST void SegSortKeysFromIndices(T* data_global, int count, const int* indices_global, int indicesCount, CudaContext& context, bool verbose = false); // Segmented sort using segment indices and supporting value exchange. template<typename KeyType, typename ValType, typename Comp> MGPU_HOST void SegSortPairsFromIndices(KeyType* keys_global, ValType* values_global, int count, const int* indices_global, int indicesCount, CudaContext& context, Comp comp, bool verbose = false); // SegSortPairsFromIndices specialized with Comp = mgpu::less<KeyType>. template<typename KeyType, typename ValType> MGPU_HOST void SegSortPairsFromIndices(KeyType* keys_global, ValType* values_global, int count, const int* indices_global, int indicesCount, CudaContext& context, bool verbose = false); //////////////////////////////////////////////////////////////////////////////// // kernels/localitysort.cuh // LocalitySortKeys is a version of MergesortKeys optimized for non-uniformly // random input arrays. If the keys begin close to their sorted destinations, // this function may exploit the structure to early-exit merge passes. // Passing verbose=true causes the function to print mergepass statistics to the // console. This may be helpful for developers to understand the performance // characteristics of the function and how effectively it early-exits merge // operations. template<typename T, typename Comp> MGPU_HOST void LocalitySortKeys(T* data_global, int count, CudaContext& context, Comp comp, bool verbose = false); // LocalitySortKeys specialized with Comp = mgpu::less<T>. template<typename T> MGPU_HOST void LocalitySortKeys(T* data_global, int count, CudaContext& context, bool verbose = false); // Locality sort supporting value exchange. template<typename KeyType, typename ValType, typename Comp> MGPU_HOST void LocalitySortPairs(KeyType* keys_global, ValType* values_global, int count, CudaContext& context, Comp comp, bool verbose = false); // LocalitySortPairs specialized with Comp = mpgu::less<T>. template<typename KeyType, typename ValType> MGPU_HOST void LocalitySortPairs(KeyType* keys_global, ValType* values_global, int count, CudaContext& context, bool verbose = false);
Segmented sort is the function that I expect to have the most immediate impact on people's applications. It addresses one facet of a serious problem facing GPU computing: we can often solve a single big problem but find it difficult to process many smaller problems in parallel.
What does the ad-hoc approach for sorting multiple variable-length arrays in parallel look like? We know how to sort keys in a thread using sorting networks. We know how to mergesort within a warp or block. MGPU Merge can take a coop
parameter to merge small lists in parallel, as long as those lists are power-of-two multiples of the block size.
As a hypothetical, perhaps we could just sort within the segment intervals. Intervals shorter than VT would be processed by an intra-thread sorting network kernel. Intervals shorter than 32 * VT would be sorted by a warp-sorting kernel. Intervals shorter than NV would be sorted by our blocksort. We could use mergesort or radix sort on longer intervals, shifting segment IDs into the most-significant bits of the key, to maintain segment stability. After all these launches we'd re-order the data back into their original segment order.
But this would be a nightmare to write and maintain.
If all we want is simplicity, we could call mergesort on individual segments using CUDA Dynamic Parallelism on sm_35 devices. However this would cause severe load-balance issues. It would perform worse than a brute-force radix sort with segment identifiers fused to the keys. Performance would degrade with the segment size—small segments would be provisioned entire CTAs, wasting huge amounts of compute.
Not knowing how to finesse problems like this is why CUDA has a reputation of being hard to program. It is infuriating that the smaller a problem gets, the more desperate the solution becomes.
MGPU Segmented Sort makes a single observation, modifies our mergesort, and delivers an implementation that is elegant and fast.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 41 67 34 0 39 24 78 58 62 64 5 81 45 27 61 91 ^ ^ ^ ^
Consider these 16 random numbers grouped into four irregular segments. Segment heads are marked with carets. We're going to launch four tiny 'blocksorts' that sort four inputs each, maintaining segment order:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 41 67 34 0 39 24 78 58 62 64 5 81 45 27 61 91 ^ ^ ^ ^ Sort into blocks of length 4: 0 1 2 3 | 4 5 6 7 | 8 9 10 11 | 12 13 14 15 0 34 41 67 | 39 24 58 78 | 62 64 5 81 | 45 27 61 91 ^ ^ ^ ^
We use induction to explain the segmented blocksort above in terms of the merge operation illustrated below. Imagine a blocksort of segmented random data as a sequence of iterative merge operations, beginning with lists of length 4.
0 1 2 3 | 4 5 6 7 | 8 9 10 11 | 12 13 14 15 0 34 41 67 | 39 24 58 78 | 62 64 5 81 | 45 27 61 91 ^ ^ ^ ^ Merge list 0 (block 0) with list 1 (block 1) Merge list 2 (block 2) with list 3 (block 3): ( 0 1 2 3 | 4 5 6 7)|( 8 9 10 11 | 12 13 14 15) ( 0 34 39 41 | 67 24 58 78)|(62 64 5 45 | 81 27 61 91) ^ ^ ^ ^ Merge list 0 (blocks 0 and 1) with list 1 (blocks 2 and 3): ( 0 1 2 3 | 4 5 6 7 | 8 9 10 11 | 12 13 14 15) ( 0 34 39 41 | 67 24 58 62 | 64 78 5 45 | 81 27 61 91) ^ ^ ^ ^
In the coop = 2
stage, where two blocks cooperatively merge pairs of lists, the green segment that spans the block 0/block 1 boundary and the green segment that spans the block 2/block 3 boundary is modified. In the coop = 4
stage, where four blocks cooperatively merge pairs of lists, the black segment that spans the block 1/block 2 boundary is modified. This fully sorts the inputs.
The observation that enables efficient segmented sorting is that only segments that span the active interface between the two input lists are modified. The active interface is where the list on the left ends and the list on the right begins. During the coop = 2
stage, the portion of the first black segment being merged into list 0 cannot be mixed with data to its left because those terms belong to the green segment; it cannot be mixed with the portion of the same segment to the right, because that data is belongs to a different output list (the boundary between block 1 and block 2 isn't an active interface until the next merge stage).
During the coop = 2 stage, only the black segment in the middle - the one that spans the interface - is modified. The other three segments are simply copied directly from source to destination.
Important: When merging lists of segmented data, only elements in the segments that span the active interface between inputs lists is modified. All other segments are locked in place and their terms are copied directly from input to output.
template<int NT, int VT, bool Stable, bool HasValues, typename KeyType, typename ValType, typename Comp> MGPU_DEVICE int2 CTASegsort(KeyType threadKeys[VT], ValType threadValues[VT], int tid, int headFlags, KeyType* keys_shared, ValType* values_shared, int* ranges_shared, Comp comp) { if(Stable) // Odd-even transpose sort. OddEvenTransposeSortFlags<VT>(threadKeys, threadValues, headFlags, comp); else // Batcher's odd-even mergesort. OddEvenMergesortFlags<VT>(threadKeys, threadValues, headFlags, comp); // Record the first and last occurrence of head flags in this segment. int blockEnd = 31 - clz(headFlags); if(-1 != blockEnd) blockEnd += VT * tid; int blockStart = ffs(headFlags); blockStart = blockStart ? (VT * tid - 1 + blockStart) : (NT * VT); ranges_shared[tid] = (int)bfi(blockEnd, blockStart, 16, 16); // Store back to shared mem. The values are in VT-length sorted lists. // These are merged recursively. DeviceThreadToShared<VT>(threadKeys, tid, keys_shared); int2 activeRange = CTASegsortLoop<NT, VT, HasValues>(threadKeys, threadValues, keys_shared, values_shared, ranges_shared, tid, make_int2(blockStart, blockEnd), comp); return activeRange; }
CTASegsort
is a reusable segmented blocksort and a generalization of CTAMergesort
. The caller provides segment head flags packed into the bitfield headFlags
. As shown in the section on sorting networks, the odd-even transposition network includes logic to support segmentation. It only swaps elements i and i+1 if the i+1 flag in the bitfield is cleared, meaning both elements are in the same segment. After the sorting network, the keys are in sorted lists with length VT.
For both segmented blocksort and the global merge passes, we build a binary tree of segment active ranges (the left- and right-most segment heads in a list). Only elements falling outside the active range can ever be modified by the sort. After the sorting network is run we calculate the active ranges (blockStart
and blockEnd
). We can now discard the head flags: the only necessary segment information for this sort is in the binary tree of ranges.
Threads use the CUDA instructions ffs
(find first set) and clz
(count leading zeros) to find the left-most and right-most segment heads in the list. These are referenced in the coordinate system of the CTA: if thread tid
has a left-most segment head at 3, blockStart
is assigned VT * tid + 3. If the thread doesn't contain a segment head, the interval (NV, -1) is used. The ranges are packed into a 32-bit integer with bfi
(bitfield insert) and stored in the auxiliary array ranges_shared
.
template<int NT, int VT, bool HasValues, typename KeyType, typename ValType, typename Comp> MGPU_DEVICE int2 CTASegsortLoop(KeyType threadKeys[VT], ValType threadValues[VT], KeyType* keys_shared, ValType* values_shared, int* ranges_shared, int tid, int2 activeRange, Comp comp) { const int NumPasses = sLogPow2<NT>::value; #pragma unroll for(int pass = 0; pass < NumPasses; ++pass) { int indices[VT]; CTASegsortPass<NT, VT>(keys_shared, ranges_shared, tid, pass, threadKeys, indices, activeRange, comp); if(HasValues) { // Exchange values through shared memory. DeviceThreadToShared<VT>(threadValues, tid, values_shared); DeviceGather<NT, VT>(NT * VT, values_shared, indices, tid, threadValues); } // Store results in shared memory in sorted order. DeviceThreadToShared<VT>(threadKeys, tid, keys_shared); } return activeRange; }
CTASegsortLoop
is a copy of CTABlocksortLoop
which forwards to CTASegsortPass
instead of CTABlocksortPass
. The segmented sort very closely follows the vanilla mergesort, both for ease-of-maintenance reasons and to help illustrate how enacting one small improvement can bring huge functionality and performance benefits to an old and simple algorithm.
template<int NT, int VT, typename T, typename Comp> MGPU_DEVICE void CTASegsortPass(T* keys_shared, int* ranges_shared, int tid, int pass, T results[VT], int indices[VT], int2& activeRange, Comp comp) { // Locate the intervals of the input lists. int3 frame = FindMergesortFrame(2<< pass, tid, VT); int a0 = frame.x; int b0 = frame.y; int listLen = frame.z; int list = tid>> pass; int listParity = 1 & list; int diag = VT * tid - frame.x; // Fetch the active range for the list this thread's list is merging with. int siblingRange = ranges_shared[1 ^ list]; int siblingStart = 0x0000ffff & siblingRange; int siblingEnd = siblingRange>> 16; // Create a new active range for the merge. int leftEnd = listParity ? siblingEnd : activeRange.y; int rightStart = listParity ? activeRange.x : siblingStart; activeRange.x = min(activeRange.x, siblingStart); activeRange.y = max(activeRange.y, siblingEnd); int p = SegmentedMergePath(keys_shared, a0, listLen, b0, listLen, leftEnd, rightStart, diag, comp); int a0tid = a0 + p; int b0tid = b0 + diag - p; SegmentedSerialMerge<VT>(keys_shared, a0tid, b0, b0tid, b0 + listLen, results, indices, leftEnd, rightStart, comp); // Store the ranges to shared memory. if(0 == diag) ranges_shared[list>> 1] = (int)bfi(activeRange.y, activeRange.x, 16, 16); }
As in CTABlocksortPass
, each thread in CTASegsortPass
calculates the range of source values (the input lists) and its cross-diagonal within that list. As in the vanilla mergesort, the input list has coordinates (a0, a1), (b0, b1), and the output list has coordinates (a0, b1).
In addition, the segmented sort computes its list index, parity, and sibling. The index is the list that the thread is mapped into for that particular pass. For pass 0 (coop = 2
), the index of thread tid is simply tid. The left- and right-most segment head positions are passed into activeRange
.
tid (a0, a1) (b0, b1): diag list parity sibling pass 0 (coop = 2): 0: ( 0, 7) ( 7, 14): 0 0 (0) 1 1: ( 0, 7) ( 7, 14): 7 1 (1) 0 2: (14, 21) (21, 28): 0 2 (0) 3 3: (14, 21) (21, 28): 7 3 (1) 2 4: (28, 35) (35, 42): 0 4 (0) 5 5: (28, 35) (35, 42): 7 5 (1) 4 6: (42, 49) (49, 56): 0 6 (0) 7 7: (42, 49) (49, 56): 7 7 (1) 6 pass 1 (coop = 4): 0: ( 0, 14) (14, 28): 0 0 (0) 1 1: ( 0, 14) (14, 28): 7 0 (0) 1 2: ( 0, 14) (14, 28): 14 1 (1) 0 3: ( 0, 14) (14, 28): 21 1 (1) 0 4: (28, 42) (42, 56): 0 2 (0) 3 5: (28, 42) (42, 56): 7 2 (0) 3 6: (28, 42) (42, 56): 14 3 (1) 2 7: (28, 42) (42, 56): 21 3 (1) 2 pass 2 (coop = 8): 0: ( 0, 28) (28, 56): 0 0 (0) 1 1: ( 0, 28) (28, 56): 7 0 (0) 1 2: ( 0, 28) (28, 56): 14 0 (0) 1 3: ( 0, 28) (28, 56): 21 0 (0) 1 4: ( 0, 28) (28, 56): 28 1 (1) 0 5: ( 0, 28) (28, 56): 35 1 (1) 0 6: ( 0, 28) (28, 56): 42 1 (1) 0 7: ( 0, 28) (28, 56): 49 1 (1) 0
The list parity
indicates what side of the merge a thread maps to: 0 for left and 1 for right. The other list in the merge is called sibling
, and it has the opposite parity. Each thread loads the packed range of its sibling list from shared memory: this is the list it is merging with. The left and right ranges are merged together to find the left- and right-most segment heads of the resulting merged list. After the segmented merge, this is stored in shared memory and recursively percolated up, building the binary tree of active ranges.
In CTASegsortPass
, leftEnd
is the location of the right-most segment in the left input list (drawn across the top of the diagram) and rightStart
the location of the left-most segment in the right list (drawn along the right side). These define the ends of the active segment. For the area outside of (leftEnd, rightStart), the Merge Path follows the x- or y-axis, as it simply copies elements from one of the inputs. The cross-diagonals are constrained to the active segment region—queries outside this region return immediately.
template<typename InputIt, typename Comp> MGPU_HOST_DEVICE int SegmentedMergePath(InputIt keys, int aOffset, int aCount, int bOffset, int bCount, int leftEnd, int rightStart, int diag, Comp comp) { // leftEnd and rightStart are defined from the origin, and diag is defined // from aOffset. // We only need to run a Merge Path search if the diagonal intersects the // segment that strides the left and right halves (i.e. is between leftEnd // and rightStart). if(aOffset + diag <= leftEnd) return diag; if(aOffset + diag >= rightStart) return aCount; bCount = min(bCount, rightStart - bOffset); int begin = max(max(leftEnd - aOffset, 0), diag - bCount); int end = min(diag, aCount); while(begin < end) { int mid = (begin + end)>> 1; int ai = aOffset + mid; int bi = bOffset + diag - 1 - mid; bool pred = !comp(keys[bi], keys[ai]); if(pred) begin = mid + 1; else end = mid; } return begin; }
SegmentedMergePath
is a straight-forward modification of the MergePath
binary search that clamps the cross-diagonals to the area defined by the active range.
template<int VT, typename T, typename Comp> MGPU_DEVICE void SegmentedSerialMerge(const T* keys_shared, int aBegin, int aEnd, int bBegin, int bEnd, T results[VT], int indices[VT], int leftEnd, int rightStart, Comp comp, bool sync = true) { bEnd = min(rightStart, bEnd); T aKey = keys_shared[aBegin]; T bKey = keys_shared[bBegin]; #pragma unroll for(int i = 0; i < VT; ++i) { bool p; // If A has run out of inputs, emit B. if(aBegin >= aEnd) p = false; else if(bBegin >= bEnd || aBegin < leftEnd) // B has hit the end of the middle segment. // Emit A if A has inputs remaining in the middle segment. p = true; else // Emit the smaller element in the middle segment. p = !comp(bKey, aKey); results[i] = p ? aKey : bKey; indices[i] = p ? aBegin : bBegin; if(p) aKey = keys_shared[++aBegin]; else bKey = keys_shared[++bBegin]; } if(sync) { __syncthreads(); } }
Four conditionals are evaluated when computing the merge predicate:
If aBegin >= aEnd
the A pointer is out-of-range so we emit the next element in B.
If bBegin >= bEnd
the B pointer is out-of-range or has hit the end of the active segment in the B list, if one exists. If the former is true, we simply emit A. If the latter is true, A must have at least entered the active segment (since A and B enter the active segment together), and A needs to complete emitting elements of the active segment before B can proceed to copy members of its inactive segments. Because the end of A's part of the active segment is also the end of A's input (see the illustration), once A finishes emitting its portion of the active segment, the first predicate, aBegin >= aEnd
, succeeds, so all remaining trips through the loop evaluate p
to false, so B's inactive segments are streamed out.
If aBegin < leftEnd
the A pointer hasn't hit the start of its active segment. To preserve segment stability, all of A's segments are emitted before merging elements of B.
Otherwise, both pointers are inside the active segment. The keys are compared and the appropriate pointer is advanced, as in an ordinary merge.
We recursively merge lists, doubling their size and halving their number with each iteration. Since the number of active segments per merge remains fixed at one (that is, the segment on the active interface), the proportion of segments that are active vanishes as the sort progresses.
Tiles that map over only non-active segments (that is, segments to the left of the right-most segment head for tiles in the left half of the merge; and segments to right of the left-most segment head for tiles in the right half of the merge) do not require any merging. These stationary tiles check a copy flag: if the flag is set, the destination is up-to-date, and there is a no-op; if the flag is not set, the kernel copies from the source to the destination and sets the up-to-date flag. Tiles that map over an active segment clear the up-to-date flag and enqueue a merge operation.
Mean segment length = 300 Merge tiles Copy tiles pass 0: 7098 ( 99.93%) 5 ( 0.07%) pass 1: 4443 ( 62.55%) 2658 ( 37.42%) pass 2: 2347 ( 33.04%) 3409 ( 47.99%) pass 3: 1168 ( 16.44%) 2275 ( 32.03%) pass 4: 572 ( 8.05%) 1168 ( 16.44%) pass 5: 302 ( 4.25%) 572 ( 8.05%) pass 6: 163 ( 2.29%) 302 ( 4.25%) pass 7: 78 ( 1.10%) 163 ( 2.29%) pass 8: 37 ( 0.52%) 78 ( 1.10%) pass 9: 18 ( 0.25%) 37 ( 0.52%) pass 10: 7 ( 0.10%) 18 ( 0.25%) pass 11: 6 ( 0.08%) 7 ( 0.10%) pass 12: 2 ( 0.03%) 6 ( 0.08%) average: 1249 ( 17.59%) 822 ( 11.59%) total : 16241 (228.65%) 10698 (150.61%) Mean segment length = 10000 Merge tiles Copy tiles pass 0: 7102 ( 99.99%) 1 ( 0.01%) pass 1: 6927 ( 97.52%) 176 ( 2.48%) pass 2: 6639 ( 93.47%) 372 ( 5.24%) pass 3: 6022 ( 84.78%) 831 ( 11.70%) pass 4: 5019 ( 70.66%) 1508 ( 21.23%) pass 5: 3758 ( 52.91%) 2084 ( 29.34%) pass 6: 2217 ( 31.21%) 2625 ( 36.96%) pass 7: 1329 ( 18.71%) 1892 ( 26.64%) pass 8: 619 ( 8.71%) 1329 ( 18.71%) pass 9: 232 ( 3.27%) 619 ( 8.71%) pass 10: 110 ( 1.55%) 232 ( 3.27%) pass 11: 148 ( 2.08%) 110 ( 1.55%) pass 12: 88 ( 1.24%) 148 ( 2.08%) average: 3093 ( 43.55%) 917 ( 12.92%) total : 40210 (566.10%) 11927 (167.91%)
SegmentedSort launch with verbose = true work reporting
The coarse-grained partitioning kernel used by MGPU Merge and Mergesort is augmented to also enqueue merge and copy work-items. The number of merge and copy tasks for a segmented sort grows with the mean segment length. In the above figure, the number of enqueued merge and copy operations is printed for each pass, for 10,000,000 inputs with average segment lengths of 300 and 10000 elements.
Once the merged list length exceeds the mean segment length, the number of merge operations required each pass begins to decrease by powers of two. The case with segment length of 300 exhibits this geometric decrease after the first global merge pass; the entire sort only requires the equivalent of only 2.28 global passes over the data. For the case with segment length of 10,000, this geometric decrease in workload takes longer to manifest, and 5.66 passes are required to sort the data.
Although this same early-exit tactic could be used within the segmented blocksort, we wouldn't expect an equivalent increase in performance there. Threads that discover that they're mapped over only non-active segments could choose to no-op, but due to SIMD execution rules, a diverged warp runs only as fast as its slowest lane. It may be feasible to dynamically reconfigure the segmented blocksort to benefit from early-exit, but that is beyond the scope of this effort.
During the global merge passes, tiles that map over fully-sorted segments (or any other 'stable' data) can early-exit rather than merging. Only CTAs that map over "active segments" (segments straddling the merge interface of two sorted input lists) need to be merged. Since the number of active segments decrees geometrically (the number of lists is cut in half each iteration as the length of the lists double), a geometrically increasing percentage of CTAs may early-exit. We implement a special work-queueing system to process active tiles on a fixed-size launch of persistent CTAs—this eliminates the start-up cost for CTAs that simply exit out.
Mergesort is an out-of-place operation. After blocksorting we load from one buffer and store to another, swap, and repeat until the data is fully sorted. A temporary buffer is allocated with enough capacity to hold a flag per tile. If the domain of the tile has the same elements in both of the double buffers, the flag is set. Early-exit with tile granularity is enabled by this status flag: if the flag is set and the tile source interval maps to the destination interval, the tile operation can be elided.
The work-queueing system supports two different operations on tiles:
Merge - If the tile's source interval (as returned by ComputeMergeRange
) does not map directly into the destination interval, we merge the two source lists and clear the tile's status flag.
Copy - If the tile's source interval maps directly into the destination interval and the status flag is cleared (indicating the source and destination tile ranges do not contain the same elements), the source interval is copied to the destination and the status flag is set.
Each global merge pass launches two kernels: the first performs global partitioning and schedules merge and copy tasks by pushing to two work queues; the second cooperatively executes all queued merge and copy tasks.
include/kernels/segmentedsort.cuh
template<int NT, bool Segments, typename KeyType, typename Comp> __global__ void KernelSegSortPartitionBase(const KeyType* keys_global, SegSortSupport support, int count, int nv, int numPartitions, Comp comp) { union Shared { int partitions[NT]; typename CTAScan<NT, ScanOpAdd>::Storage scan; }; __shared__ Shared shared; int tid = threadIdx.x; int partition = tid + (NT - 1) * blockIdx.x; // Compute one extra partition per CTA. If the CTA size is 128 threads, we // compute 128 partitions and 127 blocks. The next CTA then starts at // partition 127 rather than 128. This relieves us from having to launch // a second kernel to build the work queues. int p0; int3 frame; if(partition < numPartitions) { frame = FindMergesortFrame(2, partition, nv); int listLen = frame.z; int a0 = frame.x; int b0 = min(frame.y, count); int diag = nv * partition - a0; int aCount = min(listLen, count - a0); int bCount = min(listLen, count - b0); if(Segments) { // Segmented merge path calculation. Use the ranges as constraints. int leftRange = support.ranges_global[~1 & partition]; int rightRange = support.ranges_global[ min(numPartitions - 2, 1 | partition)]; // Unpack the left and right ranges. Transform them into the global // coordinate system by adding a0 or b0. int leftStart = 0x0000ffff & leftRange; int leftEnd = leftRange>> 16; if(nv == leftStart) leftStart = count; else leftStart += a0; if(-1 != leftEnd) leftEnd += a0; int rightStart = 0x0000ffff & rightRange; int rightEnd = rightRange>> 16; if(nv == rightStart) rightStart = count; else rightStart += b0; if(-1 != rightEnd) rightEnd += b0; if(0 == diag) support.ranges2_global[partition>> 1] = make_int2( min(leftStart, rightStart), max(leftEnd, rightEnd)); p0 = SegmentedMergePath(keys_global, a0, aCount, b0, bCount, leftEnd, rightStart, diag, comp); } else // Unsegmented merge path search. p0 = MergePath<MgpuBoundsLower>(keys_global + a0, aCount, keys_global + b0, bCount, diag, comp); shared.partitions[tid] = p0; } __syncthreads(); bool active = (tid < NT - 1) && (partition < numPartitions - 1); int p1 = active ? shared.partitions[tid + 1] : 0; __syncthreads(); DeviceSegSortCreateJob<NT>(support, count, active, frame, tid, 0, nv, partition, p0, p1, shared.partitions); }
After launching the segmented blocksort, the host launches KernelSegSortPartitionBase
for the first global merge pass or KernelSegSortPartitionDerived
for all subsequent passes. Each thread computes one partition, but since a tile needs both a starting and ending partition, a CTA of NT threads can enqueue only NT - 1 tiles.
For the base-pass kernel above, the 16-bit active ranges for each blocksort tile are unpacked and transformed by addition from tile-local coordinates to global coordinates. Neighboring active ranges (covering the two sorted lists that serve as sources for the sort's first merge pass) are then stored as int2 types in support.ranges2_global
, into which a binary tree of active ranges is constructed; this corresponds exactly to the active-range tree constructed by CTASegsortPass
in the segmented blocksort.
SegmentedMergePath
searches global data for the intersection of the Merge Path and the thread's cross-diagonal. If the cross-diagonal does not pass through the active segment (like any of the cross-diagonals that intersect the A- and B-axes as dotted lines in the segmented Merge Path figure above), the search returns immediately. As we progress in the mergesort, the percentage of cross-diagonals intersecting the active list vanishes, and we effectively benefit from early-exit of partitioning in addition to merging.
include/kernels/segmentedsort.cuh
template<int NT> MGPU_DEVICE void DeviceSegSortCreateJob(SegSortSupport support, int count, bool active, int3 frame, int tid, int pass, int nv, int block, int p0, int p1, int* shared) { typedef CTAScan<NT, ScanOpAdd> S; typename S::Storage* scan = (typename S::Storage*)shared; // Compute the gid'th work time. bool mergeOp = false; bool copyOp = false; int gid = nv * block; int4 mergeRange; if(active) { int4 range = FindMergesortInterval(frame, 2<< pass, block, nv, count, p0, p1); int a0 = range.x; int a1 = range.y; int b0 = range.z; int b1 = range.w; if(a0 == a1) { a0 = b0; a1 = b1; b0 = b1; } mergeRange = make_int4(a0, a1, b0, block); mergeOp = (b1 != b0) || (a0 != gid); copyOp = !mergeOp && (!pass || !support.copyStatus_global[block]); } int mergeTotal, copyTotal; int mergeScan = S::Scan(tid, mergeOp, *scan, &mergeTotal); int copyScan = S::Scan(tid, copyOp, *scan, ©Total); if(!tid) { shared[0] = atomicAdd(&support.queueCounters_global->x, mergeTotal); shared[1] = atomicAdd(&support.queueCounters_global->y, copyTotal); } __syncthreads(); if(mergeOp) { support.copyStatus_global[block] = 0; support.mergeList_global[shared[0] + mergeScan] = mergeRange; } if(copyOp) { support.copyStatus_global[block] = 1; support.copyList_global[shared[1] + copyScan] = block; } }
DeviceSegSortCreateJob
is called at the end of both partition kernels. This is where we put the early-exit logic. The function is passed the begin and end Merge Path indices. FindMergesortInterval
takes the Merge Path indices and computes the intervals that serve as the A and B input lists. If either A or B interval is empty, then we're loading from just one input. Since both inputs are sorted (remember we are merging pairs of sorted lists), then the tile is already sorted. In this case we elide the merge operation.
If we're on the first pass or copyStatus_global[block]
is false, then the destination buffer doesn't have the same data as the source buffer over the tile's output interval. We enqueue a copy operation by stepping on atomicAdd
and storing the tile index to copy to copyList_global
. If neither source interval is empty we must enqueue a merge by pushing the work interval to mergeList_global
and clear the status bit.
Important: The early-exit heuristic examines only the mapping of the source lists into each tile. Early-exit does not require segmentation. Segmentation is a statement of sortedness in the input, but if keys were passed in the same relative order without providing segment indices, the early-exit behavior would be identical.
Locality sort is a specialization of segmented sort that does not support (or bear the costs of) segmentation. It uses the same partitioning and queueing kernels as segmented sort, but takes a branch that uses the standard Merge Path search. Locality sort also uses the standard mergesort blocksort. Use locality sort when inputs start near to their output locations. This is a qualititative characterization of a dataset. Programmers are encouraged to try both vanilla mergesort and locality sort and use the variant with the higher throughput.
include/kernels/segmentedsort.cuh
template<typename Tuning, bool Segments, bool HasValues, typename KeyType, typename ValueType, typename Comp> MGPU_LAUNCH_BOUNDS void KernelSegSortMerge(const KeyType* keys_global, const ValueType* values_global, SegSortSupport support, int count, int pass, KeyType* keysDest_global, ValueType* valsDest_global, Comp comp) { typedef MGPU_LAUNCH_PARAMS Params; const int NT = Params::NT; const int VT = Params::VT; const int NV = NT * VT; union Shared { KeyType keys[NT * (VT + 1)]; int indices[NV]; int4 range; }; __shared__ Shared shared; int tid = threadIdx.x; // Check for merge work. while(true) { if(!tid) { int4 range = make_int4(-1, 0, 0, 0); int next = atomicAdd(&support.queueCounters_global->x, -1) - 1; if(next >= 0) range = support.mergeList_global[next]; shared.range = range; } __syncthreads(); int4 range = shared.range; __syncthreads(); if(range.x < 0) break; int block = range.w; int gid = NV * block; int count2 = min(NV, count - gid); range.w = count2 - (range.y - range.x) + range.z; if(Segments) // Segmented merge DeviceSegSortMerge<NT, VT, HasValues>(keys_global, values_global, support, tid, block, range, pass, shared.keys, shared.indices, keysDest_global, valsDest_global, comp); else // Unsegmented merge (from device/ctamerge.cuh) DeviceMerge<NT, VT, HasValues>(keys_global, values_global, keys_global, values_global, tid, block, range, shared.keys, shared.indices, keysDest_global, valsDest_global, comp); } // Check for copy work. while(true) { if(!tid) { int list = -1; int next = atomicAdd(&support.queueCounters_global->y, -1) - 1; if(next >= 0) list = support.copyList_global[next]; shared.range.x = list; } __syncthreads(); int listBlock = shared.range.x; __syncthreads(); if(listBlock < 0) break; DeviceSegSortCopy<NT, VT, HasValues>(keys_global, values_global, tid, listBlock, count, keysDest_global, valsDest_global); } }
Typically we size a grid launch to the data size. Because early-exit dynamically sizes the tasks, we'd have to copy task counts from device to host memory each iteration to change the launch size. This would synchronize the device and hurt performance. Instead we launch a fixed number of "persistent CTAs" (a small multiple of the number of SMs, queried with CudaContext::NumSMs()
) and atomically peel tasks off the queues until both queues are empty.
All threads loop until the merge queue is empty, then loop until the copy queue is empty. Thread 0 decrements the queue counter and stores a code in shared memory. After synchronization, all threads read this code out and act accordingly.
Locality sort is supported by settings Segments = false
: this directs the kernel to use DeviceMerge
rather than DeviceSegSortMerge
.


Component: SerialStartP
module SerialStartP
Author:
Thomas Schmid
Uses
interface
Boot
interface
SplitControl
as
SerialControl


Over the last year or so, I've been engaged in an exploration of differing preperations of theobroma cacao, starting with raw, bitter, baking cocoa.
The first step in beginning the experimentation was to gulp down raw baking cocoa; three teaspoons, somewhat heaping, was found to be a good, roughly-threshold dose - effective short- and long-term antidepressant effect combined with a mild, indescribable psychoactivity.
The second attempt was, naturally, a traditional(ish) preperation; several teaspoons of baking cocoa, fire sauce from Taco Bell, and hot water.
This one was quite fascinating, and remarkably pleasant. The heat of the water and the heat of the chilis combined to create a pleasant warming starting within the belly, and spreading throughout the body. The gastric inflammation from the capsacin undoubtedly aided absorbtion - and the effects were reminiscent of reports of cocaine - stimulation, top-of-the-world sensations of extreme well-being, and slight delusions of superhuman capability, all lasting half an hour to an hour.
As a side note, baking cocoa clogs the *hell* out of a coffee filter.
My humble, uneducated theory is that the increased soluability of tryptophan creates preferential conditions for seratonin synthesis and release, while the aqueous soluability of theobromine and caffine activiates cAMP-mediated stimulation, and the remainder phenethylamine amino acids add a slight coloration to the effects produced by the prior two. I would imagine - and the effects seem to hold out - that the lipid soluability of the olylethanolamine anandamide amide hydrolase (AAH) inhibitors leave their effects negligable to nonexistant in this preperation.
The second preperation was 'chocolate pseudomilk,' utilizing Mocha Mix non-dairy creamer, diluted 1:1 with water, as a dairy substitute.
The effects with this solvent/transport mediator seem to be primarily phenethylamine-based, bearing strong resemblance at high dosages (10 cups at 3tsp/cup) to a high-dose lobelia extract, or perhaps somewhat similar to a spot of ephedrine, though non-stimulant - a calming mental focusing, with nuances that only those who have consumed whopping quantities of lobelia, or who can seperate the adrenomimetic properties of ephedrine from its dopaminic release/reuptake properties can know - drunk without the alchoholic confusion, laid back from the world 'out there,' outside the self, and very, very calm.
Comparing its effects to dopaminic release agonists (lobelia) *with* seratonin enhancement (tryptophan, 100mg at a time, together in a 3 week binge of talkative childlike euphoric oblivion with *drastic* memory loss likely caused by the absence of natural neurotransmission - memory loss such as 'senses correspond to something called 'reality''), it is my belief that tryptophan is relatively insoluable in this preperation - there were no effects whatsoever which related to the tryptamine system, just a clean dopaminergenic response, if bioassay and comparison to known pharmacology is correct.
...speaking of lobelia, baking cocoa will also stave off the unpleasant aftereffects of a sizeable (0.5g evaporated tincture - several large dosings, don't take all that at once) experience.
The third preperation utilized evaporated nonfat milk. While it was brought about by an absence of Mocha Mix and a souring of the fresh milk (which, ironically, I have not yet tried - I'll have to make CocoaCheese one of these days), it was rather pleasant. Indeed, as happens so often in the never-ending human childhood of learning, growing, and exploring one's world, complete accident lead to the greatest discovery.
Again, I utilized my standard 3tsp/cup, and drank...
...and was stoned. No stimulation, no other side effects - just good 'n gently stoned, with a rapid absorbtion curve.
...unfortunately, much of my pleasing mellowing was interrupted and spent in a phone conversation with my grandfather; I had come mostly down by the time I hung up. It *is* a tad short-lasting, perhaps 30 minutes to an hour...
...I had a small portion left in the bottom of the glass, perhaps a quarter cup. I drank it...
...and was stoned again for the next hour. This is nice.
I theorize that whatever emulsification agents are commonly found in milk preferentially emulsify and transport the AAHi compounds of cocoa with drastic efficiency; the fact that it was nonfat avoided dilution. I shall definately have to look into this.
About two hours after consumption (shortly after the secondary stone wore off its peak), a milder phenethylaminesque response began to become noticeable, but quickly subsided - and was dwarfed with what appeared to be the prior cannibinoid response.
This concludes my knowledge of the differing means of preparing xocuatl at this time, and its variance in effect based on solvent. With luck, future developments will include fractional aqueous precipitation from ethanol, alkaloidal examinations, and the consumption of zwittwerionic fractions - but until then, I am lightly impoverished.
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


Charisma measures your ability to interact effectively with others. It includes such factors as confidence and eloquence, and it can represent a charming or commanding personality.
A Charisma check might arise when you try to influence or entertain others, when you try to make an impression or tell a convincing lie, or when you are navigating a tricky social situation. The Deception, Intimidation, Performance, and Persuasion skills reflect aptitude in certain kinds of Charisma checks.
Deception. Your Charisma (Deception) check determines whether you can convincingly hide the truth, either verbally or through your actions. This deception can encompass everything from misleading others through ambiguity to telling outright lies.
Intimidation. When you attempt to influence someone through overt threats, hostile actions, and physical violence, the GM might ask you to make a Charisma (Intimidation) check. Examples include trying to pry information out of a prisoner, convincing street thugs to back down from a confrontation, or using the edge of a broken bottle to convince a sneering vizier to reconsider a decision.
Performance. Your Charisma (Performance) check determines how well you can delight an audience with music, dance, acting, storytelling, or some other form of entertainment.
Persuasion. When you attempt to influence someone or a group of people with tact, social graces, or good nature, the GM might ask you to make a Charisma (Persuasion) check. Typically, you use persuasion when acting in good faith, to foster friendships, make cordial requests, or exhibit proper etiquette. Examples of persuading others include convincing a chamberlain to let your party see the king, negotiating peace between warring tribes, or inspiring a crowd of townsfolk.
Other Charisma Checks. The GM might call for a Charisma check when you try to accomplish tasks like the following:
Bards, Paladins, Sorcerers, and Warlocks use Charisma as their spellcasting ability, which helps determine the saving throw DCs of spells they cast.


| Under some national accounting requirements exploration and development costs for oil and gas properties in the development or production phases are accounted for in cost centres that include all properties in a large geographical area. Click each item to find out more. |
| Measurement of oil and gas assets |
| Accounting for items of property, plant and equipment |
| First-time adoption |


Show the Code Coverage dialog every time you launch a new run configuration with code coverage. The coverage options dialog is displayed when different coverage data has been produced.


--- layout: post tags: [Software Design] date: 2012-12-18 15:08:31 UTC title: "Zookeepers must become Rangers" description: "For want of a nail the shoe was lost... This post explains why Zookeeper developers must become Ranger developers to escape the vicious cycle of impossible deadlines and low-quality code." image: "/content/binary/more-dependencies.png" --- {% include JB/setup %}
{{ page.description }}
In a previous article I wrote about Ranger and Zookeper developers. The current article is going to make no sense to you if you haven't read the previous, but in brief, Rangers explicitly deal with versioning and forwards and backwards compatibility (henceforth called temporal compatibility) in order to produce workable code.
While reading the previous article, you may have thought that you'd rather like to be a Ranger than a Zookeeper, simply because it sounds cooler. Well, I didn't pick those names by accident :) You don't want to be a Zookeeper developer.
Zookeepers may be under the impression that, since they (or their organization) control the entire installation base of their code, they don't need to deal with the versioning aspect of software design. This is an illusion. It may seem like a little thing, but, through a chain reaction, the lack of versioning leads to impossible deadlines, slipping code quality, death marches and many unpleasant aspects of our otherwise wonderful vocation.
In order to escape the vicious cycle of low-quality-code, death marches, and firefighting, Zookeepers need to explicitly deal with versioning of the software they produce, essentially turning themselves into Rangers.
In the following, I will make two assumptions about the type of software I discuss here:
In order to understand why versioning is so important (even for Zoo Software) it's important to first understand why Zookeepers consistently disregard it.
It's sometimes a bit surprising to me that (.NET) programmers are resistant to good design. There's this tome of knowledge originally published as the Framework Design Guidelines and later published online on MSDN as the Design Guidelines for Developing Class Libraries. Even if you don't care to read through it all, tools such as Visual Studio Code Analysis and FxCop (which is free) encapsulate many of those guidelines and helps identify when your code diverges.
In my experience, Zookeeper resistance against the Framework Design Guidelines is perfectly examplified by the 'rules' related to selecting between classes and interfaces:
The problem with a guideline like this is that most Zookeepers react to such advice by thinking: "This isn't relevant for the kind of code I write. I control the entire install base of my code, so it's not a problem for me to introduce a breaking change. That entire knowledge base of design guidelines is targeted at another type of developers. I need not care about it."
Zookeepers fail to address versioning and temporal compatibility because they (incorrectly) assume that they can always schedule deployments of services and clients together in such a way that breaking changes never actually break anything. That may work as long as systems are monolithic and exist in a vacuum, but as soon as you start integrating systems, this disregard for versioning leads straight to hell.
Now that you understand why Zookeepers tend to ignore versioning it's time to understand where that attitude leads. This is best done with an example.
Imagine a team tasked with building a music catalog service for internal use. In the first release of the service a track looks like this:
<track> <name>Recovery</name> <artist>Curve</artist> <album>Come Clean</album> <length>288</length> </track>
This is obviously a naïve attempt, and a bit of planning could probably have prevented the following nasty particular surprise. However, this is just an illustrative example, and I'm sure you've found yourself in a situation where a new requirement took you entirely by surprise - no matter how much you tried to predict the future.
After the team has released the first version of the music catalog service, it turns out that some tracks may be the result of collaborations, and thus have multiple artists. The track may also appear in multiple albums (such as compilations or greatest hits collections), or on no album at all. Thus, version 2 of a track will have to look like this:
<track> <name>Under Pressure</name> <artists> <artist>David Bowie</artist> <artist>Queen</artist> </artists> <albums> <album>Hot Space</album> <album>Greatest Hits</album> <album>The Singles: 1969-1993</album> </albums> <length>242</length> </track>
This is obviously a breaking change, but since the team works in an organization where the entire installation base is under control, they work with the Enterprise Architecture team to schedule a release of this breaking change with all their clients.
However, the service was made to support other systems, so this change must be coordinated with all the known clients. It turns out that three systems depend on the music catalog service.
Each of these systems are being worked on by separate teams, each with individual deadlines. These teams are not planning to deploy new versions of their applications all on the same day. Their immediate schedules are already set, and they aren't aligned.
The Architecture team must negotiate future iterations with teams A, B, and C to line them up so that the music catalog team can release their new version on the same day.
This alignment may be months into the future - perhaps a significant fraction of a year. The music catalog team can't sit still while they wait for this date to arrive, so they work on other new features, most likely introducing other breaking changes along the way.
Meanwhile, team A goes through three iterations. In the first two iterations, they know that they are going to deploy into a production environment with version 1 of the music catalog, so they are going to need a testing environment that looks like that. For the third iteration, they know that they are going to deploy into a production environment with version 2 of the music catalog, so they are going to have to change their testing environment to reflect that fact.
Team B goes through four iterations that don't align with those of Team A. When Team A updates their testing environment to music catalog version 2, Team B is still working on an iteration where they must test against version 1. Thus, they must have their own testing environment. They can't share the testing environemtn with Team A.
The same is true for Team C: it needs its own private testing environment. Notice that this is a testing environment that not only involves configuration and maintenance of the team's own application, but also of its dependency, the music catalog service. In order to provide realistic data for performance testing, each testing environment must also be maintained with representative sample data, and may have to run on production-like hardware, in production-like network topologies. Add software licences to the mix, and you may start to realize that such a testing environment is expensive.
So far, the analysis has been unrealistically simplified. It turns out that the A, B, and C applications have other dependencies besides the music catalog service.
Each of these dependencies are also moving, producing new features. Some of those new features also involve breaking changes. Once again, the Enterprise Architecture team must negotiate a coordinated deployment date to make sure nothing breaks. However, the organization have more applications than A, B, and C. Application D, for instance, doesn't depend on the music catalog service, but it shares a dependency with application A. Thus, Team D must take part in the coordination effort, making sure that they deploy their new version on the same day as everyone else. Meanwhile, they'll need yet another one of those expensive testing environments.
I'm not making this up. I've had clients (financial institutions) that had hundreds of testing environments and big, coordinated deployments. Their applications didn't have two or three dependencies, but dozens of dependencies each - often on mainframe-based systems (very expensive).
All this leads to Big Bang Releases. Once or twice each year, all Zoo Sofware is simultaneously deployed according to a meticulously laid plan. This date becomes an unnegotiatable deadline. No team can miss the deadline, because dozens of other teams directly or indirectly depend on each other.
What happens when a Big Bang deadline grows nearer? What if you aren't ready to release? What if you just discovered a catastrophic bug?
This is what happens:
Do you think I'm exaggerating? I have seen this happen more than once. In fact, I also strongly suspect that this sort of situation recently unfolded itself within a big international ISV - at least, the symptoms are all here, for all to see :)
All this because Zookeepers don't deal with temporal compatibility.
Zookeepers must stop being Zookeepers and become Rangers. There's no excuse to not deal explicitly with versioning. You can't just introduce a breaking change in your code. The cost is often much larger than you think. The repercussions can be immense. Sometimes there's no way around breaking changes, but deal with them in tried-and-true ways. Introduce your new API side-by-side with the old API and deprecate the old API. Give clients a chance to move to the new API at their own pace. Monitor usage of your API to figure out when it's safe to completely remove the deprecated API. There are many well-described ways to deal with versioning and temporal compatibility. This is not an article about how to evolve an API, but rather a call to action: explicitly address versioning, or face the consequences!
Looser coupling can help. What I've described here is simply a form of tight temporal coupling. Still, even with loosely coupled, asynchronous, messaging-based architectures, messages must still be explicitly versioned to avoid problems.
Once an orginazation has moved entirely to a loosely coupled, explicitly versioned architecture where each team can use Continuous Delivery, they may find that they don't need that Enterprise Achitecture team at all :)
Comments
Thanks for sharing,
Jason
In such a case, one had to change an acceptance test if a service could not deal with legacy clients.
A team that calls itself agile and does not use the tools and techniques necessary for agile development, is just a bunch of cowboy coders, but neither "Zookeepers" nor "Rangers".
The point of the article (IMO) is that developers need to learn a new tool that you haven't listed - namely good internal version handling (and to start thinking of internal collaborators in the same way as external).
A tricky thing for an organisation is to decide when make the move from (easy-to-write) temporally coupled code to discrete services (with increased versioning and infrastructure overheads). Most places with this problem have started off small, grow quickly by bolting bits onto the existing architecture, and by the time someone switched on enough to start changing the organisation takes charge, it is too late to make the switch easily. TBH most places I've worked where this is the root cause of the problems, the Enterprise Architect team can't see the wood for the trees.
However, the other Markus is spot on. The message of this article isn't that you should use TDD, Continuous Integration, etc. Lot's of people have already said that before me. The message is that even if you are an 'internal' developer, you must think explicitly about versioning and backwards compatibility.


RT @PragTob: Still sad how many people see @jruby and think "Java! Enterprise! AbstractFactory! Maven! No not for me..." It's a totally dif…
5/26/2014, 9:56:37 AM
Favs: 0
Retweets: 0
link--- layout: tweet ---
RT @PragTob: Still sad how many people see @jruby and think "Java! Enterprise! AbstractFactory! Maven! No not for me..." It's a totally dif…
5/26/2014, 9:56:37 AM
Favs: 0
Retweets: 0
link


{% extends "base.html" %} {% load static %} {% block site_title %}Logout{% endblock %} {% block body_class %}app flex-row align-items-center{% endblock %} {% block body_content %}
Thank you for using Stormrose. Your session has been closed, so you can now safely close your browser window.
Want to stay? Take me back to the Dashboard


nixTrackApiClient
nix.track-api-client
Used to make calls to nutritionix api. Low level function to build api client on top of. All service methods are wrappers around service-function calls.
nixTrackApiClient(endpoint, config);
| Param | Type | Details |
|---|---|---|
| endpoint | string | Relative api endpoint url e.g. '/users' |
| config | object | Call-specific override for http calls configuration object https://docs.angularjs.org/api/ng/service/$http#usage The last override in chain of default object and nix.track-api-client.provider:nixTrackApiClientProvider#methods_sethttpconfig to form final http call config. Default object is built like that: { method: 'GET', url: apiEndpoint + endpoint, headers: { "x-user-jwt": client.getUserJwt(), 'X-APP-ID': credentials.appId, 'X-APP-KEY': credentials.appKey }, params: {} } |
Set api credentials generated at https://developer.nutritionix.com portal
| Param | Type | Details |
|---|---|---|
| appId | string | Application id |
| appKey | string | Application Key |
Set service-wide override for http calls configuration object https://docs.angularjs.org/api/ng/service/$http#usage
| Param | Type | Details |
|---|---|---|
| value | object | configuration object compatible with https://docs.angularjs.org/api/ng/service/$http#usage |
| merge | boolean | If true extends config, otherwise overrides it |
Set current user jwt
| string|function | User jwt or callable to fetch it within current application |
208 is an id of calories nutrient
List of ids of nutrients considered as Macronutrients


See LocationAdded 4 days ago
Description: Howard building is a glorious four floor building in the middle of Strand London. It has a parking facility and is pet friendly. It has a shopping plaza which is 100 m away and has 3 commute spots around it including the rail.


Blot creates a folder in your Dropbox and publishes files you put inside. These files become blog posts automatically:
You can organize your folder however you like. There are three folders which Blot treats differently:
Blot automatically generates a publish date, permalink, thumbnail and a list of tags for each blog post. However, you can specify this metadata yourself:
Date: February 28th, 1973
Permalink: gravity
# Gravity's Rainbow
A screaming comes across the sky.
Metadata must start on the first line and be separated from the rest of the post with a blank line.
You can specify custom metadata and access it in your blog's template.
You can create a page elsewhere in your blog's folder by adding Page: yes
to the file's metadata. If you'd like to create a page that isn't on your blog's menu, add Menu: no
as well.
You can specify an entry's publish date in the metadata at the top of a file. Blot supports plenty of formats. The following dates are equivalent:
23/06/2012 23.6.12 23-6-2012 2012-6-23 00:00
You can use the file's path to specify an entry's date. The entries for the following files have a publish date of midnight on February 23rd, 2016:
/2016/2/23/post.txt /2016/2.23.post.txt 2016_02_23_00:00_post.txt
If you order your dates differently please tell Blot on the settings page. If you change an entry's date to some time in future, Blot will wait until then to publish it.
You can specify an entry's permalink in the metadata at the top of a file. For example, to create a blog post with the permalink example.com/raspberry
, add this:
Permalink: raspberry
Permalinks can contain slashes and accents. If you're migrating to Blot from a different platform, consider setting up redirects instead.
You can specify tags in the metadata at the top of a file:
Tags: Literature, Book reviews
You can also use folders to tag posts. For example, files in a folder called [Literature]
will become blog posts tagged 'Literature'. You can nest tag folders:
[Literature] / [Book reviews] / Lamiel.txt
Blot attempts to generate a thumbnail for each blog post. By default, Blot will use the largest JPEG or PNG image in your blog post. You can override this and specify a path or URL to an image in the metadata at the top of a file. If you use a path, make sure the image is inside Blot's folder.
If your blog post has no image, then your blog post will not have a thumbnail. Learn how to use these thumbnails in your template.


{% include "current/includes/patient-details/_header.html" %} Patient details Primary information {% include "current/includes/_person.html" %} Secondary information Postcode of residential address Find address I don't know the postcode Building and street Town or city County Relevant occupation Add any information about the patient's work history that could of contributed to their death Organisation responsible for care prior to death Funeral arrangements Cremation Buried at sea Burial Repatriation Unknown Does the patient have any devices implanted that could cause issues at cremation? Yes No Unknown Enter details below Funeral directors Personal effects Is there any property to collect from the ward? Yes No Unknown Enter details below Bereaved or representative information Enter information regarding the patient's bereaved family or representative handling their affairs. Full name Relationship Present at death? Yes No Unknown Phone number Informed of death? Yes No Unknown Appointment date Day Month Year Enter any additional details Add another Request for rapid release Select a reason Faith death Coroner case Child death Cultural death Other Enter any further relevant details Full name Relationship Present at death? Yes No Unknown Phone number Informed of death? Yes No Unknown Appointment date Day Month Year Remove above
Enter information regarding the patient's bereaved family or representative handling their affairs.


--- layout: default_cz --- {% include nav_cz.html %} {% include header_cz.html %} {% include about_cz.html %} {% include actuality_cz.html %} {% include camps_portfolio_cz.html %} {% include sponsors_cz.html %}


Debugging Matlab on Mac¶
If matlab throws errors like this:
Invalid MEX-file
'/Users/johannesschoeneberg/git/JohSchoeneberg/pyLattice/src/matlab/trackingModule/dependencies/u-track/software/mex/createDistanceMatrix.mexmaci64':
dlopen(/Users/johannesschoeneberg/git/JohSchoeneberg/pyLattice/src/matlab/trackingModule/dependencies/u-track/software/mex/createDistanceMatrix.mexmaci64,
6): Library not loaded: @loader_path/libmex.dylib
Referenced from:
/Users/johannesschoeneberg/git/JohSchoeneberg/pyLattice/src/matlab/trackingModule/dependencies/u-track/software/mex/createDistanceMatrix.mexmaci64
Reason: image not found.
The solution can be found here:
https://stackoverflow.com/questions/48458660/library-not-loaded-loader-path-libmex-dylib-in-matlab
First, find out your matlab version:
find /Applications/ -maxdepth 1 -type d -name 'MAT*'
In my case this is:
MATLAB_R2017b.app
In the subsequent steps, the 2017b matlab version is used. If you have a different version, replace MATLAB_R2017b.app
with your version below.
You have to add the libraries to your matlab startup script:
/Applications/MATLAB_R2017b.app/bin/.matlab7rc.sh.
Add:
DYLD_LIBRARY_PATH="/Applications/MATLAB_R2017b.app/bin/maci64:/Applications/MATLAB_R2017b.app/sys/os/maci64"
Then startup matlab from the command line:
/Applications/MATLAB_R2017b.app/bin/matlab


Only call a function once
When called multiple times it will return the return value from the first call.
Unlike the module once, this one isn’t naughty extending Function.prototype
.
$ npm install --save onetime
```js let i = 0;
const foo = onetime(() => i++);
foo(); //=> 0 foo(); //=> 0 foo(); //=> 0 ```
Type: function
Function that should only be called once.
Type: boolean
Default: false
Set to true
if you want it to fail with a nice and descriptive error when called more than once.
MIT © Sindre Sorhus


Coverage Summary for Class: R (android.support.v7.cardview)
| Class | Method, % | Line, % |
|---|---|---|
| R | 0% (0/ 1) | 0% (0/ 1) |
| R$attr | 0% (0/ 1) | 0% (0/ 1) |
| R$color | 0% (0/ 1) | 0% (0/ 1) |
| R$dimen | 0% (0/ 1) | 0% (0/ 1) |
| R$style | 0% (0/ 1) | 0% (0/ 1) |
| R$styleable | 0% (0/ 2) | 0% (0/ 2) |
| total | 0% (0/ 7) | 0% (0/ 7) |
1 /* AUTO-GENERATED FILE. DO NOT MODIFY.
2 *
3 * This class was automatically generated by the
4 * gradle plugin from the resource data it found. It
5 * should not be modified by hand.
6 */
7 package android.support.v7.cardview;
8
9 public final class R {
10 public static final class attr {
11 public static final int cardBackgroundColor = 0x7f040057;
12 public static final int cardCornerRadius = 0x7f040058;
13 public static final int cardElevation = 0x7f040059;
14 public static final int cardMaxElevation = 0x7f04005a;
15 public static final int cardPreventCornerOverlap = 0x7f04005b;
16 public static final int cardUseCompatPadding = 0x7f04005c;
17 public static final int cardViewStyle = 0x7f04005d;
18 public static final int contentPadding = 0x7f040081;
19 public static final int contentPaddingBottom = 0x7f040082;
20 public static final int contentPaddingLeft = 0x7f040083;
21 public static final int contentPaddingRight = 0x7f040084;
22 public static final int contentPaddingTop = 0x7f040085;
23 }
24 public static final class color {
25 public static final int cardview_dark_background = 0x7f06003f;
26 public static final int cardview_light_background = 0x7f060040;
27 public static final int cardview_shadow_end_color = 0x7f060041;
28 public static final int cardview_shadow_start_color = 0x7f060042;
29 }
30 public static final class dimen {
31 public static final int cardview_compat_inset_shadow = 0x7f070054;
32 public static final int cardview_default_elevation = 0x7f070055;
33 public static final int cardview_default_radius = 0x7f070056;
34 }
35 public static final class style {
36 public static final int Base_CardView = 0x7f100026;
37 public static final int CardView = 0x7f1000c2;
38 public static final int CardView_Dark = 0x7f1000c3;
39 public static final int CardView_Light = 0x7f1000c4;
40 }
41 public static final class styleable {
42 public static final int[] CardView = { 0x0101013f, 0x01010140, 0x7f040057, 0x7f040058, 0x7f040059, 0x7f04005a, 0x7f04005b, 0x7f04005c, 0x7f040081, 0x7f040082, 0x7f040083, 0x7f040084, 0x7f040085 };
43 public static final int CardView_android_minWidth = 0;
44 public static final int CardView_android_minHeight = 1;
45 public static final int CardView_cardBackgroundColor = 2;
46 public static final int CardView_cardCornerRadius = 3;
47 public static final int CardView_cardElevation = 4;
48 public static final int CardView_cardMaxElevation = 5;
49 public static final int CardView_cardPreventCornerOverlap = 6;
50 public static final int CardView_cardUseCompatPadding = 7;
51 public static final int CardView_contentPadding = 8;
52 public static final int CardView_contentPaddingBottom = 9;
53 public static final int CardView_contentPaddingLeft = 10;
54 public static final int CardView_contentPaddingRight = 11;
55 public static final int CardView_contentPaddingTop = 12;
56 }
57 }


Traditional - Ireland
This game (also called the Celtic "Game of the Kings") is played on a 6x7 square board with the following setup:
|
| An example The green soldier at c5 (the cell with the green dot) jumped to e5 and becomes a King. Black soldier at e3 cannot jump to e1, because it would disconnect f3 from all the other pieces on the board. |


{{- partial "head.html" . -}}
{{- partial "header.html" . -}}{{ .description }}
You are able to create this jumbotron, similar to previous versions of Bootstrap.
Swap the background-color utility and add a `.text-*` color utility to mix up the jumbotron look. Then, mix and match with your favourite choice.
You can also keep it light and add a border for some added definition to the boundaries of your content.
Latest Article


--- layout: post status: publish published: true title: BizTalk Error Question author: display_name: Tomas Restrepo login: tomasr email<EMAIL_ADDRESS>url: http://winterdom.com/ author_login: tomasr author_email<EMAIL_ADDRESS>author_url: http://winterdom.com/ wordpress_id: 657 wordpress_url: http://winterdom.com/2004/03/biztalkerrorquestion date: '2004-03-08 19:43:50 +0000' date_gmt: '2004-03-08 19:43:50 +0000' categories: - BizTalk tags: [] comments: [] ---
Does anyone know exactly what one has to do to use fault messages on request/receive ports in BizTalk Server 2004 succesfully without running into a "X2162: must receive before sending a fault message on an implemented port" error at compile time?
(I really don't have to mention at this point that the XLang/S compiler error messages don't appear to be documented anywhere, do I?)


Goldcoast
The Gold Coast is a metropolitan region south of Brisbane on Australia’s east coast. It's famed for its long sandy beaches, surfing spots and elaborate system of inland canals and waterways. It’s also home to theme parks such as Dreamworld, Sea World and Wet’n’Wild. Inland, hiking trails crisscross Lamington National Park’s mountain ridges and valleys, home to rare birds and rainforest.


Join the numbers to reach the 2048 tile. This is a customized game with the "explode" dynamic.
Customized by: Songyee Park


Rebekah Vardy shares baby scan photo after surprise shower
Rebekah Vardy melted the hearts of her Instagram followers by sharing a photo of her baby scan, days after being thrown a surprise shower.
The 37-year-old is nearing the end of her pregnancy as she expects her third child with husband Jamie Vardy, and gave her followers a glimpse of the unborn child.

Rebekah tagged the See Your Baby Instagram account, which offers 4D scans so expectant parents can get a closer look at their unborn child.
In September, she shared a video of one of her scans which featured the baby’s heartbeat flashing on the screen.
Earlier this week, the former I’m A Celebrity star was surprised with a baby shower thrown by her sister.
Advertisement Advertisement
The party was decorated with bouquets of flowers, personalised balloons and cake-pops.
‘Such an amazing surprise… Thank you to my amazing Sister. Love you all xxxx,’ Rebekah wrote alongside photos from the special day.
Leicester City footballer Jamie commented: ‘You deserve it xxxx,’ while her pal Nicola McLean posted a string of love heart emojis.
Rebekah and Jamie are already parents to daughter Sofia and son Finlay, while the WAG has two children from previous relationships, and Jamie is father to daughter Ella with his ex-girlfriend Emma Daggett.
It’s going to be quite the large family and we don’t envy the sleepless nights that are coming.
The last few weeks of Rebekah’s pregnancy haven’t been the easiest, with the WAG finding herself in the midst of a very public feud with Coleen Rooney, who accused Rebekah’s Instagram account of leaking fake stories about her.
However, Rebekah vehemently denied being responsible for selling stories to newspapers and has hired a legal team to conduct a thorough investigation.
Got a showbiz story?
If you've got a story, video or pictures get in touch with the Metro.co.uk Entertainment team by emailing us<EMAIL_ADDRESS>calling 020 3615 2145 or by visiting our Submit Stuff page - we'd love to hear from you.


[gecode-users] SUBSCRIBE modification event required?
Christian Schulte
cschulte at kth.se
Thu Sep 17 17:37:44 CEST 2015
Hi Joe,
Gosh, you have been digging deep, haven't you? So the thing is, if you only have a variable type with three modification events, then the SUBSCRIBE event becomes the ASSIGNED one. This I just reverse-engineered from some ultra-ugly perl script that generates the code from the .vis files. There I saw that the script even has a syntax error not caught by perl ;-)
And yes, of course this should be documented and the error should be caught. I will do it for the trunk soonish.
Today seems to be .vis day on this mailing list!
Cheers
Christian
--
Christian Schulte, www.gecode.org/~schulte
Professor of Computer Science, KTH, cschulte at kth.se
Expert Researcher, SICS, cschulte at sics.se
-----Original Message-----
From: users-bounces at gecode.org [mailto:users-bounces at gecode.org] On Behalf Of Joseph Scott
Sent: Thursday, September 17, 2015 04:54 PM
To: users at gecode.org
Subject: [gecode-users] SUBSCRIBE modification event required?
Hi,
I was just rebuilding Gecode with an included .vis file for a variable type, and I neglected to designate one of the modification events as the SUBSCRIBE event, resulting in the error message included below[1].
Adding "=SUBSCRIBE" to one of the ModEvent definitions makes it go away, as one would expect.
But now I'm curious. MPG very carefully does not state that one must define a modification event for new subscriptions. Quite the opposite,
really: of the four special modification events, the other three are explicitly stated to be required (p. 403). What is more, it seems that bool.vis does not specify a subscription event for Boolean variables (upon reflection, this is unsurprising). So it seems like I should be able to omit this specification — although, I admit, I can't see why I would want to do so for anything other than a Boolean.
So my question is this: am I required to specify a modification event for subscriptions to (non-assigned) variables? And if so, is this requirement somehow waived for Booleans?
Thanks!
Joe
[1] using g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2:
g++ -I. -fcx-limited-range -fno-signaling-nans -fno-rounding-math
-ffinite-math-only -fno-math-errno -fno-strict-aliasing -O3
-fvisibility=hidden -ggdb -pipe -Wall -Wextra -fPIC -pthread -DNDEBUG \
-c -o gecode/kernel/archive.o gecode/kernel/archive.cpp In file included from ./gecode/kernel.hh:228:0,
from gecode/kernel/archive.cpp:38:
./gecode/kernel/var-imp.hpp: In member function ‘void StrVars::Str::StrVarImpBase::subscribe(Gecode::Space&,
Gecode::Propagator&, Gecode::PropCond, bool, bool)’:
./gecode/kernel/var-imp.hpp:463:79: error: expected primary-expression before ‘,’ token
Gecode::VarImp<StrVars::Str::StrVarImpConf>::subscribe(home,p,pc,assigned,,schedule);
^
_______________________________________________
Gecode users mailing list
users at gecode.org
https://www.gecode.org/mailman/listinfo/gecode-users
More information about the users
mailing list


I am a Full-Stack Web Developer that has the dream to become part of a group of people who love to code. Well, it's pretty simple, I take software development really seriously and I enjoy the whole process I take to built something great, even those small details that the user doesn't care about.
Focused and passionate. I like to write, test and refactor my code every moment of the development time and I value every process that can guarantee the quality of the applications I build.
All the things I wish to attract to my life as a programmer are linked to the Web and Mobile development. I'll improve myself to apply TDD in all my projects. And I'll make sure to be always open to learning the new stuff that comes out frequently in the software development world.
Generally, I build web solutions with Angular. I'm using Angular since the start of my career, and it has been a great experience. This framework gives me innumerable possibilities to build something reliable and robust, not to mention that writing tests with it have been a second nature.
Since I started all with Angular and got really familiar with Typescript, I decided to get this power I was tasting and start to construct mobile applications with it. Then, naturally, Ionic Framework came into the scene. Well, it only got better, now I could use the same good practices and testing environment to build hybrid apps.
Sorry! I am too picky with my solutions and I don't have something complete to display yet.
But check my GitHub repository anyway!


View White Progressive Insurance Logo UK. Choose industry relevant colors for your insurance company logo template. Business and personal insurance solutions travelers insurance.
Working as an insurance agent at progressive 124 reviews. Check out our analysis of an insurance company logo design. Find the latest news from and about progressive insurance, including news releases, coverage and insurance information customized for reporters.
Get 50+ designs to choose from with 100% money back guarantee.
Progressive insurance catalli insurance brokers. Logo progressive white jamieson insurance agency inc. Progressive casualty insurance company is responsible for this page. Photo about collection of logos and brands of most popular insurance companies on white tablet on rusty wooden background.


--- layout: post title: "What's new at Krystal Pharma Engineering" subtitle: "New Services." date: 2018-01-13 23:45:13 -0400 background: '/img/posts/01.jpg' ---
These are the new changes


Spinner
Compact default
Compact colorful
Compact dark
Compact light
Compact contrast
Full Colorful
Full Dark
Full Light
Full Contrast
Default


Case against three for recording, circulating video
by Special CorrespondentThe Jayapura Police in Koppa taluk, on Monday, booked a case against three persons on the charge of video recording two adults and circulating the video clip on social media.
Harish Pandey, SP, in a press release on Monday, said a group of three, on November 12, allegedly video-recorded the two individuals, assaulted them with canes and robbed them of ₹1,500. Though the couple requested the accused not to make the clip public, they circulated it on social media.
Based on the complaint received by the youth, the Jayapura police registered the case. They are trying to nab the accused, the SP said.


Basic TSC
Chartbag of timeseriescharts
TSC with 2 axes
TSC with subplots, varying # of axes, and sparkline
Labels
Bar Renderer


--- layout: newsstory title: THREE 2-2'S IN A ROW match: Yeltz 2 Bath 2 ---
Another game, another 2-2 draw and another point nearer safety. It all sounds a bit boring , but watching the mighty Yeltz is anything but that at the moment, with brilliant attacking being cancelled out by some less than brilliant defending.
We started off like the proverbial train, so it was inevitable when they took the lead completely against the run of play through the superbly-named Fidell Richards. Thankfully we finally equalised just before the break when super-striker Rob Elmes was left unmarked to nod in a Neil Smith cross.
The second-half was more of the same; we piled forward, hitting the post, blazing it over, some mad scrambles, going behind again and then getting a late equaliser when Stevo smashed in a shot from the edge of the box into the roof of the net.
I'm off on me hols for a few days over Easter, so the site may be a bit out of date (no news there then), but I'll do what I can .


ExponentialTransformer¶
-
class
variable_transformers.
ExponentialTransformer
(exp=0.5, variables=None)[source]¶ Applies the exponential transformation to numerical variables.
The transformer works only with numerical variables.
Parameters: variables (list) – The list of numerical variables that will be discretised. If none, it defaults to all numerical type variables. -
fit
(X, y=None)[source]¶ Learns the numerical variables.
Parameters: - X (pandas dataframe of shape = [n_samples, n_features]) – The training input samples. Should be the entire dataframe, not just seleted variables.
- y (None) – y is not needed in this transformer, yet the sklearn pipeline API requires this parameter for checking. You can either leave it as None or pass y.
-
transform
(X)[source]¶ Applies the exponential transformation to the variables.
Parameters: X (pandas dataframe of shape = [n_samples, n_features]) – The input samples. Returns: X_transformed – The dataframe with exponentially transformed variables. Return type: pandas dataframe of shape = [n_samples, n_features]
-


| PostgreSQL 9.0.10 Documentation | ||||
|---|---|---|---|---|
| Prev | Up | Chapter 5. Data Definition | Next |
A PostgreSQL database cluster contains one or more named databases. Users and groups of users are shared across the entire cluster, but no other data is shared across databases. Any given client connection to the server can access only the data in a single database, the one specified in the connection request.
Note: Users of a cluster do not necessarily have the privilege to access every database in the cluster. Sharing of user names means that there cannot be different users named, say, joe in two databases in the same cluster; but the system can be configured to allow joe access to only some of the databases.
A database contains one or more named schemas, which in turn contain tables. Schemas also contain other kinds of named objects, including data types, functions, and operators. The same object name can be used in different schemas without conflict; for example, both schema1 and myschema can contain tables named mytable. Unlike databases, schemas are not rigidly separated: a user can access objects in any of the schemas in the database he is connected to, if he has privileges to do so.
There are several reasons why one might want to use schemas:
To allow many users to use one database without interfering with each other.
To organize database objects into logical groups to make them more manageable.
Third-party applications can be put into separate schemas so they do not collide with the names of other objects.
Schemas are analogous to directories at the operating system level, except that schemas cannot be nested.
To create a schema, use the CREATE SCHEMA command. Give the schema a name of your choice. For example:
CREATE SCHEMA myschema;
To create or access objects in a schema, write a qualified name consisting of the schema name and table name separated by a dot:
schema.table
This works anywhere a table name is expected, including the table modification commands and the data access commands discussed in the following chapters. (For brevity we will speak of tables only, but the same ideas apply to other kinds of named objects, such as types and functions.)
Actually, the even more general syntax
database.schema.table
can be used too, but at present this is just for pro forma compliance with the SQL standard. If you write a database name, it must be the same as the database you are connected to.
So to create a table in the new schema, use:
CREATE TABLE myschema.mytable ( ... );
To drop a schema if it's empty (all objects in it have been dropped), use:
DROP SCHEMA myschema;
To drop a schema including all contained objects, use:
DROP SCHEMA myschema CASCADE;
See Section 5.11 for a description of the general mechanism behind this.
Often you will want to create a schema owned by someone else (since this is one of the ways to restrict the activities of your users to well-defined namespaces). The syntax for that is:
CREATE SCHEMA schemaname AUTHORIZATION username;
You can even omit the schema name, in which case the schema name will be the same as the user name. See Section 5.7.6 for how this can be useful.
Schema names beginning with pg_ are reserved for system purposes and cannot be created by users.
In the previous sections we created tables without specifying any schema names. By default such tables (and other objects) are automatically put into a schema named "public". Every new database contains such a schema. Thus, the following are equivalent:
CREATE TABLE products ( ... );
and:
CREATE TABLE public.products ( ... );
Qualified names are tedious to write, and it's often best not to wire a particular schema name into applications anyway. Therefore tables are often referred to by unqualified names, which consist of just the table name. The system determines which table is meant by following a search path, which is a list of schemas to look in. The first matching table in the search path is taken to be the one wanted. If there is no match in the search path, an error is reported, even if matching table names exist in other schemas in the database.
The first schema named in the search path is called the current schema. Aside from being the first schema searched, it is also the schema in which new tables will be created if the CREATE TABLE command does not specify a schema name.
To show the current search path, use the following command:
SHOW search_path;
In the default setup this returns:
search_path -------------- "$user",public
The first element specifies that a schema with the same name as the current user is to be searched. If no such schema exists, the entry is ignored. The second element refers to the public schema that we have seen already.
The first schema in the search path that exists is the default location for creating new objects. That is the reason that by default objects are created in the public schema. When objects are referenced in any other context without schema qualification (table modification, data modification, or query commands) the search path is traversed until a matching object is found. Therefore, in the default configuration, any unqualified access again can only refer to the public schema.
To put our new schema in the path, we use:
SET search_path TO myschema,public;
(We omit the $user here because we have no immediate need for it.) And then we can access the table without schema qualification:
DROP TABLE mytable;
Also, since myschema is the first element in the path, new objects would by default be created in it.
We could also have written:
SET search_path TO myschema;
Then we no longer have access to the public schema without explicit qualification. There is nothing special about the public schema except that it exists by default. It can be dropped, too.
See also Section 9.23 for other ways to manipulate the schema search path.
The search path works in the same way for data type names, function names, and operator names as it does for table names. Data type and function names can be qualified in exactly the same way as table names. If you need to write a qualified operator name in an expression, there is a special provision: you must write
OPERATOR(schema.operator)
This is needed to avoid syntactic ambiguity. An example is:
SELECT 3 OPERATOR(pg_catalog.+) 4;
In practice one usually relies on the search path for operators, so as not to have to write anything so ugly as that.
By default, users cannot access any objects in schemas they do not own. To allow that, the owner of the schema must grant the USAGE privilege on the schema. To allow users to make use of the objects in the schema, additional privileges might need to be granted, as appropriate for the object.
A user can also be allowed to create objects in someone else's schema. To allow that, the CREATE privilege on the schema needs to be granted. Note that by default, everyone has CREATE and USAGE privileges on the schema public. This allows all users that are able to connect to a given database to create objects in its public schema. If you do not want to allow that, you can revoke that privilege:
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
(The first "public" is the schema, the second "public" means "every user". In the first sense it is an identifier, in the second sense it is a key word, hence the different capitalization; recall the guidelines from Section 4.1.1.)
In addition to public and user-created schemas, each database contains a pg_catalog schema, which contains the system tables and all the built-in data types, functions, and operators. pg_catalog is always effectively part of the search path. If it is not named explicitly in the path then it is implicitly searched before searching the path's schemas. This ensures that built-in names will always be findable. However, you can explicitly place pg_catalog at the end of your search path if you prefer to have user-defined names override built-in names.
In PostgreSQL versions before 7.3, table names beginning with pg_ were reserved. This is no longer true: you can create such a table name if you wish, in any non-system schema. However, it's best to continue to avoid such names, to ensure that you won't suffer a conflict if some future version defines a system table named the same as your table. (With the default search path, an unqualified reference to your table name would then be resolved as the system table instead.) System tables will continue to follow the convention of having names beginning with pg_, so that they will not conflict with unqualified user-table names so long as users avoid the pg_ prefix.
Schemas can be used to organize your data in many ways. There are a few usage patterns that are recommended and are easily supported by the default configuration:
If you do not create any schemas then all users access the public schema implicitly. This simulates the situation where schemas are not available at all. This setup is mainly recommended when there is only a single user or a few cooperating users in a database. This setup also allows smooth transition from the non-schema-aware world.
You can create a schema for each user with the same name as that user. Recall that the default search path starts with $user, which resolves to the user name. Therefore, if each user has a separate schema, they access their own schemas by default.
If you use this setup then you might also want to revoke access to the public schema (or drop it altogether), so users are truly constrained to their own schemas.
To install shared applications (tables to be used by everyone, additional functions provided by third parties, etc.), put them into separate schemas. Remember to grant appropriate privileges to allow the other users to access them. Users can then refer to these additional objects by qualifying the names with a schema name, or they can put the additional schemas into their search path, as they choose.
In the SQL standard, the notion of objects in the same schema being owned by different users does not exist. Moreover, some implementations do not allow you to create schemas that have a different name than their owner. In fact, the concepts of schema and user are nearly equivalent in a database system that implements only the basic schema support specified in the standard. Therefore, many users consider qualified names to really consist of username.tablename. This is how PostgreSQL will effectively behave if you create a per-user schema for every user.
Also, there is no concept of a public schema in the SQL standard. For maximum conformance to the standard, you should not use (perhaps even remove) the public schema.
Of course, some SQL database systems might not implement schemas at all, or provide namespace support by allowing (possibly limited) cross-database access. If you need to work with those systems, then maximum portability would be achieved by not using schemas at all.