Hello,
I've run some tests with memxor on a x86-64 machine. My results are:
* C implementation (compiled with gcc 4.4):
Xoring in chunks of 32768 bytes: done. 50.09 Gb in 5.00 secs:
10.02 Gb/sec
Xoring (unaligned) in chunks of 32768 bytes: done. 39.90 Gb in
5.00 secs: 7.98 Gb/sec
* ASM implementation:
Xoring in chunks of 32768 bytes: done. 38.32 Gb in 5.00 secs:
7.66 Gb/sec
Xoring (unaligned) in chunks of 32768 bytes: done. 30.16 Gb in
5.00 secs: 6.03 Gb/sec
It seems that in x86-64 the ASM version is slower than the C one.
Moreover I noticed that the loop unrolling techniques used in the C
code have no visible performance benefit.
However, an SSE2 version of memxor (attached) increases performance by
30% or more in the same CPU.
* SSE2:
Xoring in chunks of 32768 bytes: done. 69.94 Gb in 5.00 secs:
13.98 Gb/sec
Xoring (unaligned) in chunks of 32768 bytes: done. 65.96 Gb in
5.00 secs: 13.19 Gb/sec
regards,
Nikos
/* memxor.c
*
*/
/* nettle, low-level cryptographics library
*
* Copyright (C) 1991, 1993, 1995 Free Software Foundation, Inc.
* Copyright (C) 2010 Niels Möller
*
* The nettle library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The nettle library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the nettle library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
/* Implementation inspired by memcmp in glibc, contributed to the FSF
by Torbjorn Granlund.
*/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <limits.h>
#include <emmintrin.h>
#include "memxor.h"
typedef __m128i word_t;
#if SIZEOF_LONG & (SIZEOF_LONG - 1)
#error Word size must be a power of two
#endif
#define ALIGN_OFFSET(p) ((uintptr_t) (p) % sizeof(word_t))
#define WORD_T_THRESH 16
/* XOR word-aligned areas. n is the number of words, not bytes. */
static void
memxor_common_alignment (word_t *dst, const word_t *src, size_t n)
{
__m128i xmm1,xmm2;
for (; n > 0; dst += 1, src += 1, n--)
{
xmm1 = _mm_load_si128(dst);
xmm2 = _mm_load_si128(src);
xmm1 = _mm_xor_si128(xmm1, xmm2);
_mm_store_si128(dst, xmm1);
}
}
/* XOR *un-aligned* src-area onto aligned dst area. n is number of
words, not bytes. Assumes we can read complete words at the start
and end of the src operand. */
static void
memxor_different_alignment (word_t *dst, const word_t *src, size_t n)
{
__m128i xmm1,xmm2;
for (; n > 0; dst += 1, src += 1, n--)
{
xmm1 = _mm_loadu_si128(dst);
xmm2 = _mm_loadu_si128(src);
xmm1 = _mm_xor_si128(xmm1, xmm2);
_mm_storeu_si128(dst, xmm1);
}
}
/* Performance, Intel SU1400 (x86_64): 0.25 cycles/byte aligned, 0.45
cycles/byte unaligned. */
/* XOR LEN bytes starting at SRCADDR onto DESTADDR. Result undefined
if the source overlaps with the destination. Return DESTADDR. */
uint8_t *
memxor(uint8_t *dst, const uint8_t *src, size_t n)
{
uint8_t *orig_dst = dst;
if (n >= WORD_T_THRESH)
{
/* There are at least some bytes to compare. No need to test
for N == 0 in this alignment loop. */
while (ALIGN_OFFSET (dst))
{
*dst++ ^= *src++;
n--;
}
if (ALIGN_OFFSET (src))
memxor_different_alignment ((word_t *) dst, (const word_t*)src, n / sizeof(word_t));
else
memxor_common_alignment ((word_t *) dst, (const word_t *) src, n / sizeof(word_t));
dst += n & -SIZEOF_LONG;
src += n & -SIZEOF_LONG;
n = n & (SIZEOF_LONG - 1);
}
for (; n > 0; n--)
*dst++ ^= *src++;
return orig_dst;
}
/* cryptodev_test - simple benchmark tool for cryptodev
*
* Copyright (C) 2010 by Phil Sutter <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <signal.h>
#include <unistd.h>
static double udifftimeval(struct timeval start, struct timeval end)
{
return (double)(end.tv_usec - start.tv_usec) +
(double)(end.tv_sec - start.tv_sec) * 1000 * 1000;
}
static int must_finish = 0;
static void alarm_handler(int signo)
{
must_finish = 1;
}
static void value2human(double bytes, double time, double* data, double* speed,char* metric)
{
if (bytes > 1000 && bytes < 1000*1000) {
*data = ((double)bytes)/1000;
*speed = *data/time;
strcpy(metric, "Kb");
return;
} else if (bytes >= 1000*1000 && bytes < 1000*1000*1000) {
*data = ((double)bytes)/(1000*1000);
*speed = *data/time;
strcpy(metric, "Mb");
return;
} else if (bytes >= 1000*1000*1000) {
*data = ((double)bytes)/(1000*1000*1000);
*speed = *data/time;
strcpy(metric, "Gb");
return;
} else {
*data = (double)bytes;
*speed = *data/time;
strcpy(metric, "bytes");
return;
}
}
void xor(void)
{
unsigned char *buffer1, *buffer2, *buffer3;
static int val = 23;
struct timeval start, end;
double total = 0;
double secs, ddata, dspeed;
char metric[16];
int i = 23;
int chunksize = 32*1024;
buffer1 = malloc(chunksize+1);
buffer3 = malloc(chunksize+1);
memset(buffer1, i, chunksize+1);
memset(buffer3, i, chunksize+1);
buffer2 = malloc(chunksize);
memset(buffer2, 0x78, chunksize);
/* sanity check first */
memxor(buffer3, buffer2, chunksize);
for (i=0;i<chunksize;i++)
if (buffer3[i] != (buffer1[i] ^ buffer2[i])) {
fprintf(stderr, "memxor is broken! %x found, expected %x ^ %x = %x\n", buffer3[i], buffer1[i], buffer2[i], buffer1[i]^buffer2[i]);
abort();
}
memcpy(buffer3, buffer1, chunksize+1);
memxor(buffer3+1, buffer2, chunksize);
for (i=0;i<chunksize;i++)
if (buffer3[i+1] != (buffer1[i+1] ^ buffer2[i])) {
fprintf(stderr, "memxor (unaligned) is broken! in pos %d, found %x, expected %x ^ %x = %x\n", i, buffer3[i+1], buffer1[i+1], buffer2[i], buffer1[i+1]^buffer2[i]);
abort();
}
printf("\tXoring in chunks of %d bytes: ", chunksize);
fflush(stdout);
must_finish = 0;
alarm(5);
gettimeofday(&start, NULL);
do {
size_t output_size;
memxor(buffer1, buffer2, chunksize);
total+=chunksize;
} while(must_finish==0);
gettimeofday(&end, NULL);
secs = udifftimeval(start, end)/ 1000000.0;
value2human(total, secs, &ddata, &dspeed, metric);
printf ("done. %.2f %s in %.2f secs: ", ddata, metric, secs);
printf ("%.2f %s/sec\n", dspeed, metric);
printf("\tXoring (unaligned) in chunks of %d bytes: ", chunksize);
fflush(stdout);
must_finish = 0;
total = 0;
alarm(5);
gettimeofday(&start, NULL);
do {
size_t output_size;
memxor(buffer1+1, buffer2, chunksize);
total+=chunksize;
} while(must_finish==0);
gettimeofday(&end, NULL);
secs = udifftimeval(start, end)/ 1000000.0;
value2human(total, secs, &ddata, &dspeed, metric);
printf ("done. %.2f %s in %.2f secs: ", ddata, metric, secs);
printf ("%.2f %s/sec\n", dspeed, metric);
return;
}
int main(void)
{
int fd, i;
signal(SIGALRM, alarm_handler);
xor();
return 0;
}
_______________________________________________
nettle-bugs mailing list
[email protected]
http://lists.lysator.liu.se/mailman/listinfo/nettle-bugs