Speed up matrix-matrix multiplication using SSE vector instructions - c

I have some trouble in vectorize some C code using SSE vector instructions. The code which I have to victorize is
#define N 1000
void matrix_mul(int mat1[N][N], int mat2[N][N], int result[N][N])
{
int i, j, k;
for (i = 0; i < N; ++i)
{
for (j = 0; j < N; ++j)
{
for (k = 0; k < N; ++k)
{
result[i][k] += mat1[i][j] * mat2[j][k];
}
}
}
}
Here is what I got so far:
void matrix_mul_sse(int mat1[N][N], int mat2[N][N], int result[N][N])
{
int i, j, k; int* l;
__m128i v1, v2, v3;
v3 = _mm_setzero_si128();
for (i = 0; i < N; ++i)
{
for (j = 0; j < N; j += 4)
{
for (k = 0; k < N; k += 4)
{
v1 = _mm_set1_epi32(mat1[i][j]);
v2 = _mm_loadu_si128((__m128i*)&mat2[j][k]);
v3 = _mm_add_epi32(v3, _mm_mul_epi32(v1, v2));
_mm_storeu_si128((__m128i*)&result[i][k], v3);
v3 = _mm_setzero_si128();
}
}
}
}
After execution I got wrong result. I know that the reason is the loading from memory to v2. I loop through mat1 in row major order so I need to load mat2[0][0], mat2[1][0], mat2[2][0], mat2[3][0].... but what actually loaded is mat2[0][0], mat2[0][1], mat2[0][2], mat2[0][3]... because mat2 has stored in the memory in row major order. I tried to fix this problem but without any improvement.
Can anyone help me please.

Below fixed your implementation:
void matrix_mul_sse(int mat1[N][N], int mat2[N][N], int result[N][N])
{
int i, j, k;
__m128i v1, v2, v3, v4;
for (i = 0; i < N; ++i)
{
for (j = 0; j < N; ++j) // 'j' must be incremented by 1
{
// read mat1 here because it does not use 'k' index
v1 = _mm_set1_epi32(mat1[i][j]);
for (k = 0; k < N; k += 4)
{
v2 = _mm_loadu_si128((const __m128i*)&mat2[j][k]);
// read what's in the result array first as we will need to add it later to our calculations
v3 = _mm_loadu_si128((const __m128i*)&result[i][k]);
// use _mm_mullo_epi32 here instead _mm_mul_epi32 and add it to the previous result
v4 = _mm_add_epi32(v3, _mm_mullo_epi32(v1, v2));
// store the result
_mm_storeu_si128((__m128i*)&result[i][k], v4);
}
}
}
}
In short _mm_mullo_epi32 (requires SSE4.1) produces 4 x int32 results as opposed to _mm_mul_epi32 which does 2 x int64 results. If you cannot use SSE4.1 then have a look at the answer here for an alternative SSE2 solution.
Full description by Intel Intrinsic Guide:
_mm_mullo_epi32: Multiply the packed 32-bit integers in a and b, producing intermediate 64-bit integers, and store
the low 32 bits of the intermediate integers in dst.
_mm_mul_epi32: Multiply the low 32-bit integers from each packed 64-bit element in a and b, and store the
signed 64-bit results in dst.

I kinda changed around your code to make the addressing explicit [ it helps in this case ].
#define N 100
This is a stub for the vector unit multiple & accumulate operation; you should be able to replace NV with whatever throw your vector unit has, and put the relevant opcodes in here.
#define NV 8
int Vmacc(int *A, int *B) {
int i = 0;
int x = 0;
for (i = 0; i < NV; i++) {
x += *A++ * *B++;
}
return x;
}
This multiply has two notable variations from the norm:
1. It caches the columnar vector into a contiguous one.
2. It attempts to push slices of the multiply accumulate into a vector-like func.
Even without using the vector unit, this takes half the time of naive version just because of better cache/prefetch utilization.
void mm2(int *A, int *B, int n, int *C) {
int c, r;
int stride = 0;
int cache[N];
for (c = 0; c < n; c++) {
/* cache cumn i: */
for (r = 0; r < n; r++) {
cache[r] = B[c + r*n];
}
for (r = 0; r < n; r++) {
int k = 0;
int x = 0;
int *Av = A + r*n;
for (k = 0; k+NV-1 < n; k += NV) {
x += Vmacc(Av+k, cache+k);
}
while (k < n) {
x += Av[k] * cache[k];
k++;
}
C[r*n + c] = x;
}
}
}

Related

As a result of processing arrays -nan(ind)

I am writing a program that creates arrays of a given length and manipulates them. You cannot use other libraries.
First, an array M1 of length N is formed, after which an array M2 of length N is formed/2.
In the M1 array, the division by Pi operation is applied to each element, followed by elevation to the third power.
Then, in the M2 array, each element is alternately added to the previous one, and the tangent modulus operation is applied to the result of addition.
After that, exponentiation is applied to all elements of the M1 and M2 array with the same indexes and the resulting array is sorted by dwarf sorting.
And at the end, the sum of the sines of the elements of the M2 array is calculated, which, when divided by the minimum non-zero element of the M2 array, give an even number.
The problem is that the result X gives is -nan(ind). I can't figure out exactly where the error is.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
const int A = 441;
const double PI = 3.1415926535897931159979635;
inline void dwarf_sort(double* array, int size) {
size_t i = 1;
while (i < size) {
if (i == 0) {
i = 1;
}
if (array[i - 1] <= array[i]) {
++i;
}
else
{
long tmp = array[i];
array[i] = array[i - 1];
array[i - 1] = tmp;
--i;
}
}
}
inline double reduce(double* array, int size) {
size_t i;
double min = RAND_MAX, sum = 0;
for (i = 0; i < size; ++i) {
if (array[i] < min && array[i] != 0) {
min = array[i];
}
}
for (i = 0; i < size; ++i) {
if ((int)(array[i] / min) % 2 == 0) {
sum += sin(array[i]);
}
}
return sum;
}
int main(int argc, char* argv[])
{
int i, N, j;
double* M1 = NULL, * M2 = NULL, * M2_copy = NULL;
double X;
unsigned int seed = 0;
N = atoi(argv[1]); /* N равен первому параметру командной строки */
M1 = malloc(N * sizeof(double));
M2 = malloc(N / 2 * sizeof(double));
M2_copy = malloc(N / 2 * sizeof(double));
for (i = 0; i < 100; i++)
{
seed = i;
srand(i);
/*generate*/
for (j = 0; j < N; ++j) {
M1[j] = (rand_r(&seed) % A) + 1;
}
for (j = 0; j < N / 2; ++j) {
M2[j] = (rand_r(&seed) % (10 * A)) + 1;
}
/*map*/
for (j = 0; j < N; ++j)
{
M1[j] = pow(M1[j] / PI, 3);
}
for (j = 0; j < N / 2; ++j) {
M2_copy[j] = M2[j];
}
M2[0] = fabs(tan(M2_copy[0]));
for (j = 0; j < N / 2; ++j) {
M2[j] = fabs(tan(M2[j] + M2_copy[j]));
}
/*merge*/
for (j = 0; j < N / 2; ++j) {
M2[j] = pow(M1[j], M2[j]);
}
/*sort*/
dwarf_sort(M2, N / 2);
/*sort*/
X = reduce(M2, N / 2);
}
printf("\nN=%d.\n", N);
printf("X=%f\n", X);
return 0;
}
Knowledgeable people, does anyone see where my mistake is? I think I'm putting the wrong data types to the variables, but I still can't solve the problem.
Replace the /* merge */ part with this:
/*merge*/
for (j = 0; j < N / 2; ++j) {
printf("%f %f ", M1[j], M2[j]);
M2[j] = pow(M1[j], M2[j]);
printf("%f\n", M2[j]);
}
This will print the values and the results of the pow operation. You'll see that some of these values are huge resulting in an capacity overflow of double.
Something like pow(593419.97, 31.80) will not end well.

Adding two numbers [1, 10^10000] as arrays of chars - C

I tackled the problem by first figuring out the length of two given numbers and aligning the one with less digits (if one exists) into a new array so that the ones, tens, hundreds etc. align with the bigger number's ones, tens, hundreds, etc.
Then I wanted to save the sum of each two aligned elements (with a mod of 10) into a new array while checking if the sum of digits is greater than 10 - just the basic sum stuff. Now the problem occurs with adding two elements into the aplusb integer and I've tried fixing it with writing
int aplusb = (lengthA[max-i]-'0') +(temp[max-i]-'0');
but it doesn't work. I'm stuck and I don't know what to do. Please help.
The whole code:
#include <stdio.h>
#include <math.h>
int main(){
char a[10000];
char b[10000];
scanf("%s %s", &a, &b);
char sum[10000];
int lengthA = 0;
int lengthB = 0;
int i = 0;
while(a[i]){
i++;
} lengthA = i;
i = 0;
while(b[i]){
i++;
} lengthB = i;
char temp[10000];
int aplusb;
int carry = 0;
int max = lengthA;
int difference = abs(lengthA - lengthB);
if(lengthA>lengthB){
for(i=0; i<lengthA; i++){
temp[i+difference]=b[i];
}
for(i=0; i<=max; i++){
aplusb = lengthA[max-i]+temp[max-i]; //<-- this is the problematic line
if(carry = 1) aplusb++;
if(aplusb>9){
carry = 1;
aplusb%=10;
}
sum[i]=aplusb;
}
}
for(i=0; i<=max; i++){
printf("%c", sum[i]);
}
/*
if(lengthB>lengthA){
max = lengthB;
for(i=0; i<lengthB; i++){
temp[i+difference]=a[i];
}
}*/
return 0;
}
Doing operations and storing on very large numbers is very akin to doing operations and storing polynomials, i.e. with x = 10. a0 + a1.10 + a2.10^2 ... + an.10^n.
There are many polynomial libraries on the Internet, where you could find inspiration. All operations on your very large numbers can be expressed in terms of polynomials. This means that by using base 2^8, or even base 2^63, instead of base 10 to internally store your large numbers you would greatly improve performance.
You must also normalize your coefficients after operations to keep them positive. Operations may result in a negative coefficient, That can easily be fixed, as it is very similar to borrowing after a subtraction, this means coefficients must be larger than your base by 1bit.
To convert back to base 10, you'd need to solve r (your result) for v (your value), such as r(10)=v(2^63). This has only one solution, if you enforce the positive coefficients rule.
[note] After thinking about it some more: the rule on positive coefficients may only be necessary for printing, after all.
Example: adding. no memory error checking
int addPolys(signed char** result, int na, const signed char* a, int nb, const signed char* b)
{
int i, nr, nmin, carry, *r;
nr = max(na, nb) + 1;
nmin = min(na, nb);
r = malloc(sizeof(signed char) * (na + nb + 1));
if (nb < na)
{
nr = nb;
}
for (i = 0; i < nmin; ++i)
{
r[i] = a[i] + b[i];
}
for (; i < na; ++i)
{
r[i] = a[i];
}
for (; i < nb; ++i)
{
r[i] = b[i];
}
r[nr - 1] = 0;
// carry - should really be a proc of its own, unoptimized
carry = 0;
for (i = 0; i < nr; ++i)
{
r[i] += carry;
if (r[i] > 10)
{
carry = r[i] / 10;
r[i] %= 10;
}
else if (r[i] < 0)
{
carry = (r[i] / 10) - 1;
r[i] -= (carry * 10);
}
else
carry = 0;
}
// 'remove' leading zeroes
for (i = nr - 1; i > 0; --i)
{
if (r[i] != 0) break;
}
++i;
*result = r;
if (i != nr)
{
*result = realloc(i * sizeof(signed char));
}
return i; // return number of digits (0 being 1 digit long)
}
That code is working now for any two positive numbers with up to ten thousand digits:
#include <stdio.h>
#include <math.h>
#include <string.h>
int main(){
char chara[10000];
char charb[10000];
scanf("%s %s", &chara, &charb);
int lengthA = strlen(chara);
int lengthB = strlen(charb);
int max = lengthA;
if(lengthB>lengthA) max=lengthB;
int dif = abs(lengthA - lengthB);
//ustvari int tabele
int a[max];
int b[max];
int sum[max+1];
// nastavi nule
int i;
for(i=0; i<max; i++){
a[i] = 0;
b[i] = 0;
sum[i] = 0;
} sum[max] = 0;
//prekopiraj stevila iz char v int tabele &obrni vrstni red
for(i=0; i<lengthA; i++){
a[i] = chara[lengthA-i-1]-'0';
}
for(i=0; i<lengthB; i++){
b[i] = charb[lengthB-i-1]-'0';
}
int vsota;
int prenos = 0;
for(i=0; i<max; i++){
vsota = a[i]+b[i] + prenos;
if(vsota>=10) prenos = 1;
else if (vsota<10) prenos = 0;
sum[i]=vsota%10;
}
if(prenos==1){
sum[max] = 1;
for(i = max; i>=0; i--){
printf("%d", sum[i]);
}
} else {
for(i = max-1; i>=0; i--){
printf("%d", sum[i]);
}
}
return 0;
}

Radix Sort Base 16 (Hexadecimals)

I have spent more 10hr+ on trying to sort the following(hexadecimals) in LSD radix sort, but no avail. There is very little material on this subject on web.
0 4c7f cd80 41fc 782c 8b74 7eb1 9a03 aa01 73f1
I know I have to mask and perform bitwise operations to process each hex digit (4 bits), but have no idea on how and where.
I'm using the code (I understand) from GeeksforGeeks
void rsort(int a[], int n) {
int max = getMax(a, n);
for (int exp = 1; max / exp > 0; exp *= 10) {
ccsort(a, n, exp);
}
}
int getMax(int a[], int n) {
int max = a[0];
int i = 0;
for (i = 0; i < n; i++) {
if (a[i] > max) {
max = a[i];
}
}
return max;
}
void ccsort(int a[], int n, int exp) {
int count[n];
int output[n];
int i = 0;
for (i = 0; i < n; i++) {
count[i] = 0;
output[i] = 0;
}
for (i = 0; i < n; i++) {
++count[(a[i] / exp) % 10];
}
for (i = 1; i <= n; i++) {
count[i] += count[i - 1];
}
for (i = n - 1; i >= 0; i--) {
output[count[(a[i] / exp) % 10] - 1] = a[i];
--count[(a[i] / exp) % 10];
}
for (i = 0; i < n; i++) {
a[i] = output[i];
}
}
I have also checked all of StackOverFlow on this matter, but none of them covers the details.
Your implementation of radix sort is slightly incorrect:
it cannot handle negative numbers
the array count[] in function ccsort() should have a size of 10 instead of n. If n is smaller than 10, the function does not work.
the loop for cumulating counts goes one step too far: for (i = 1; i <= n; i++). Once again the <= operator causes a bug.
you say you sort by hex digits but the code uses decimal digits.
Here is a (slightly) improved version with explanations:
void ccsort(int a[], int n, int exp) {
int count[10] = { 0 };
int output[n];
int i, last;
for (i = 0; i < n; i++) {
// compute the number of entries with any given digit at level exp
++count[(a[i] / exp) % 10];
}
for (i = last = 0; i < 10; i++) {
// update the counts to have the index of the place to dispatch the next
// number with a given digit at level exp
last += count[i];
count[i] = last - count[i];
}
for (i = 0; i < n; i++) {
// dispatch entries at the right index for its digit at level exp
output[count[(a[i] / exp) % 10]++] = a[i];
}
for (i = 0; i < n; i++) {
// copy entries batch to original array
a[i] = output[i];
}
}
int getMax(int a[], int n) {
// find the largest number in the array
int max = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > max) {
max = a[i];
}
}
return max;
}
void rsort(int a[], int n) {
int max = getMax(a, n);
// for all digits required to express the maximum value
for (int exp = 1; max / exp > 0; exp *= 10) {
// sort the array on one digit at a time
ccsort(a, n, exp);
}
}
The above version is quite inefficient because of all the divisions and modulo operations. Performing on hex digits can be done with shifts and masks:
void ccsort16(int a[], int n, int shift) {
int count[16] = { 0 };
int output[n];
int i, last;
for (i = 0; i < n; i++) {
++count[(a[i] >> shift) & 15];
}
for (i = last = 0; i < 16; i++) {
last += count[i];
count[i] = last - count[i];
}
for (i = 0; i < n; i++) {
output[count[(a[i] >> shift) & 15]++] = a[i];
}
for (i = 0; i < n; i++) {
a[i] = output[i];
}
}
void rsort16(int a[], int n) {
int max = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > max) {
max = a[i];
}
}
for (int shift = 0; (max >> shift) > 0; shift += 4) {
ccsort16(a, n, shift);
}
}
It would be approximately twice as fast to sort one byte at a time with a count array of 256 entries. It would also be faster to compute the counts for all digits in one pass, as shown in rcgldr's answer.
Note that this implementation still cannot handle negative numbers.
There's a simpler way to implement a radix sort. After checking for max, find the lowest power of 16 >= max value. This can be done with max >>= 4 in a loop, incrementing x so that when max goes to zero, then 16 to the power x is >= the original max value. For example a max of 0xffff would need 4 radix sort passes, while a max of 0xffffffff would take 8 radix sort passes.
If the range of values is most likely to take the full range available for an integer, there's no need to bother determining max value, just base the radix sort on integer size.
The example code you have shows a radix sort that scans an array backwards due to the way the counts are converted into indices. This can be avoided by using an alternate method to convert counts into indices. Here is an example of a base 256 radix sort for 32 bit unsigned integers. It uses a matrix of counts / indices so that all 4 rows of counts are generated with just one read pass of the array, followed by 4 radix sort passes (so the sorted data ends up back in the original array). std::swap is a C++ function to swap the pointers, for a C program, this can be replaced by swapping the pointers inline. t = a; a = b; b = t, where t is of type uint32_t * (ptr to unsigned 32 bit integer). For a base 16 radix sort, the matrix size would be [8][16].
// a is input array, b is working array
uint32_t * RadixSort(uint32_t * a, uint32_t *b, size_t count)
{
size_t mIndex[4][256] = {0}; // count / index matrix
size_t i,j,m,n;
uint32_t u;
for(i = 0; i < count; i++){ // generate histograms
u = a[i];
for(j = 0; j < 4; j++){
mIndex[j][(size_t)(u & 0xff)]++;
u >>= 8;
}
}
for(j = 0; j < 4; j++){ // convert to indices
m = 0;
for(i = 0; i < 256; i++){
n = mIndex[j][i];
mIndex[j][i] = m;
m += n;
}
}
for(j = 0; j < 4; j++){ // radix sort
for(i = 0; i < count; i++){ // sort by current lsb
u = a[i];
m = (size_t)(u>>(j<<3))&0xff;
b[mIndex[j][m]++] = u;
}
std::swap(a, b); // swap ptrs
}
return(a);
}
void int_radix_sort(void) {
int group; //because extracting 8 bits
int buckets = 1 << 8; //using size 256
int map[buckets];
int mask = buckets - 1;
int i;
int cnt[buckets];
int flag = NULL;
int partition;
int *src, *dst;
for (group = 0; group < 32; group += 8) {
// group = 8, number of bits we want per round, we want 4 rounds
// cnt
for (int i = 0; i < buckets; i++) {
cnt[i] = 0;
}
for (int j = 0; j < n; j++) {
i = (lst[j] >> group) & mask;
cnt[i]++;
tmp[j] = lst[j];
}
//map
map[0] = 0;
for (int i = 1; i < buckets; i++) {
map[i] = map[i - 1] + cnt[i - 1];
}
//move
for (int j = 0; j < n; j++) {
i = (tmp[j] >> group) & mask;
lst[map[i]] = tmp[j];
map[i]++;
}
}
}
After hours of researching I came across the answer. I'm still do not understand what is going on in this code/answer. I cannot get my head wrapped around the concept. Hopefully, someone can explain.
I see your points. I think negative numbers are easy to sort after the list has been sorted with something like loop, flag, and swap. wb unsigned float points? – itproxti Nov 1 '16 at 16:02
As for handling floating points there might be a way, for example 345.768 is the number, it needs to be converted to an integer, i.e. make it 345768, I multiplied 1000 with it. Just like the offset moves the -ve numbers to +ve domain, so will multiplying by 1000, 10000 etc will turn the floats to numbers with their decimal part as all zeros. Then they can be typecasted to int or long. However with large values, the whole reformed number may not be accomodated within the entire int or long range.
The number that is to be multiplied has to be constant, just like the offset so that the relationship among the magnitudes is preserved. Its better to use powers of 2 such as 8 or 16, as then bitshifting operator can be used. However just like the calculation of offset takes some time, so will calculation of the multiplier will take some time. The whole array is to be searched to calculate the least number that when multiplied will turn all the numbers with zeros in decimal parts.
This may not compute fast but still can do the job if required.

Vector equations in C

Im trying to code this function which computes the projection formula
P(x) = x + (c- a dot x)a * (1/|a|^2). Note that x and a are vectors and c is a scalar. Also note that a dot x is the product dot/inner product of a and x. Here is what I have,
double dotProduct(double *q, double *b, int length) {
double runningSum = 0;
for (int index = 0; index < length; index++)
runningSum += q[index] * b[index];
return runningSum;
}
void project(double *x ,int n, double *a, double c)
{ double m_sum = 0.0;
for (int i = 0; i < n; i++) {
m_sum += a[i]*a[i];
}
for ( int j = 0; j < n; j++) {
x[j] = x[j] + (1/ m_sum)* (c - dotProduct(a, x, n))*a[j];
}
}
So my question is how do I create a test file to check the consistency of the project function since it doesn’t return anything. Is my code even right? does not return anything.

Sum of differences in array

Is there a more efficient way to achieve this:
Given an array A of size n and two positive integers a and b, find the sum floor(abs(A[i]-A[j])*a/b) taken over all pairs (i, j) where 0 <= i < j < n.
int A[n];
int a, b; // assigned some positive integer values
...
int total = 0;
for (int i = 0; i < n; i++) {
for (int j = i+1; j < n; j++) {
total += abs(A[i]-A[j])*a/b; // want integer division here
}
}
To optimize this a little bit, I sorted the array (O(nlogn)) and then didn't use an abs function. Also, I cached the value a[i] before the inner for loop, so I could just read stuff from A sequentially. I was considering precomputing a/b and storing that in a float, but the extra casting just makes it slower (especially since I want to take the floor of the result).
I couldn't come up with a solution that was better than O(n^2).
Yes, there is a more efficient algorithm. It can be done it in O(n*log n). I don't expect there to be an asymptotically faster way, but I'm far from any idea of a proof.
Algorithm
First sort the array in O(n*log n) time.
Now, let us look at the terms
floor((A[j]-A[i])*a/b) = floor ((A[j]*a - A[i]*a)/b)
for 0 <= i < j < n. For each 0 <= k < n, write A[k]*a = q[k]*b + r[k] with 0 <= r[k] < b.
For A[k] >= 0, we have q[k] = (A[k]*a)/b and r[k] = (A[k]*a)%b with integer division, for A[k] < 0, we have q[k] = (A[k]*a)/b - 1 and r[k] = b + (A[k]*a)%b unless b divides A[k]*a, in which case we have q[k] = (A[k]*a)/b and r[k] = 0.
Now we rewrite the terms:
floor((A[j]*a - A[i]*a)/b) = floor(q[j] - q[i] + (r[j] - r[i])/b)
= q[j] - q[i] + floor((r[j] - r[i])/b)
Each q[k] appears k times with positive sign (for i = 0, 1, .. , k-1) and n-1-k times with negative sign (for j = k+1, k+2, ..., n-1), so its total contribution to the sum is
(k - (n-1-k))*q[k] = (2*k+1-n)*q[k]
The remainders still have to be accounted for. Now, since 0 <= r[k] < b, we have
-b < r[j] - r[i] < b
and floor((r[j]-r[i])/b) is 0 when r[j] >= r[i] and -1 when r[j] < r[i]. So
n-1
∑ floor((A[j]-A[i])*a/b) = ∑ (2*k+1-n)*q[k] - inversions(r)
i<j k=0
where an inversion is a pair (i,j) of indices with 0 <= i < j < n and r[j] < r[i].
Calculating the q[k] and r[k] and summing the (2*k+1-n)*q[k] is done in O(n) time.
It remains to efficiently count the inversions of the r[k] array.
For each index 0 <= k < n, let c(k) be the number of i < k such that r[k] < r[i], i.e. the number of inversions in which k appears as the larger index.
Then obviously the number of inversions is ∑ c(k).
On the other hand, c(k) is the number of elements that are moved behind r[k] in a stable sort (stability is important here).
Counting these moves, and hence the inversions of an array is easy to do while merge-sorting it.
Thus the inversions can be counted in O(n*log n) too, giving an overall complexity of O(n*log n).
Code
A sample implementation with a simple unscientific benchmark (but the difference between the naive quadratic algorithm and the above is so large that an unscientific benchmark is conclusive enough).
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
long long mergesort(int *arr, unsigned elems);
long long merge(int *arr, unsigned elems, int *scratch);
long long nosort(int *arr, unsigned elems, long long a, long long b);
long long withsort(int *arr, unsigned elems, long long a, long long b);
int main(int argc, char *argv[]) {
unsigned count = (argc > 1) ? strtoul(argv[1],NULL,0) : 1000;
srand(time(NULL)+count);
long long a, b;
b = 1000 + 9000.0*rand()/(RAND_MAX+1.0);
a = b/3 + (b-b/3)*1.0*rand()/(RAND_MAX + 1.0);
int *arr1, *arr2;
arr1 = malloc(count*sizeof *arr1);
arr2 = malloc(count*sizeof *arr2);
if (!arr1 || !arr2) {
fprintf(stderr,"Allocation failed\n");
exit(EXIT_FAILURE);
}
unsigned i;
for(i = 0; i < count; ++i) {
arr1[i] = 20000.0*rand()/(RAND_MAX + 1.0) - 2000;
}
for(i = 0; i < count; ++i) {
arr2[i] = arr1[i];
}
long long res1, res2;
double start = clock();
res1 = nosort(arr1,count,a,b);
double stop = clock();
printf("Naive: %lld in %.3fs\n",res1,(stop-start)/CLOCKS_PER_SEC);
start = clock();
res2 = withsort(arr2,count,a,b);
stop = clock();
printf("Sorting: %lld in %.3fs\n",res2,(stop-start)/CLOCKS_PER_SEC);
return EXIT_SUCCESS;
}
long long nosort(int *arr, unsigned elems, long long a, long long b) {
long long total = 0;
unsigned i, j;
long long m;
for(i = 0; i < elems-1; ++i) {
m = arr[i];
for(j = i+1; j < elems; ++j) {
long long d = (arr[j] < m) ? (m-arr[j]) : (arr[j]-m);
total += (d*a)/b;
}
}
return total;
}
long long withsort(int *arr, unsigned elems, long long a, long long b) {
long long total = 0;
unsigned i;
mergesort(arr,elems);
for(i = 0; i < elems; ++i) {
long long q, r;
q = (arr[i]*a)/b;
r = (arr[i]*a)%b;
if (r < 0) {
r += b;
q -= 1;
}
total += (2*i+1LL-elems)*q;
arr[i] = (int)r;
}
total -= mergesort(arr,elems);
return total;
}
long long mergesort(int *arr, unsigned elems) {
if (elems < 2) return 0;
int *scratch = malloc((elems + 1)/2*sizeof *scratch);
if (!scratch) {
fprintf(stderr,"Alloc failure\n");
exit(EXIT_FAILURE);
}
return merge(arr, elems, scratch);
}
long long merge(int *arr, unsigned elems, int *scratch) {
if (elems < 2) return 0;
unsigned left = (elems + 1)/2, right = elems-left, i, j, k;
long long inversions = 0;
inversions += merge(arr, left, scratch);
inversions += merge(arr+left,right,scratch);
if (arr[left] < arr[left-1]) {
for(i = 0; i < left; ++i) {
scratch[i] = arr[i];
}
i = 0; j = 0; k = 0;
int *lptr = scratch, *rptr = arr+left;
while(i < left && j < right) {
if (rptr[j] < lptr[i]) {
arr[k++] = rptr[j++];
inversions += (left-i);
} else {
arr[k++] = lptr[i++];
}
}
while(i < left) arr[k++] = lptr[i++];
}
return inversions;
}

Resources