This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <sys/mman.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <sys/time.h> | |
#include <fcntl.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
#include <arpa/inet.h> | |
#if defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) | |
# error architecture must be little endian | |
#endif | |
#if defined(_byteswap_uint64) || (defined(_MSC_VER) && _MSC_VER >= 1400) | |
# define _ntohll(x) (_byteswap_uint64(x)) | |
#elif defined(bswap_64) | |
# define _ntohll(x) bswap_64(x) | |
#elif defined(__DARWIN_OSSwapInt64) | |
# define _ntohll(x) __DARWIN_OSSwapInt64(x) | |
#else | |
# warn _ntohll by bitshift | |
# define _ntohll(x) \ | |
( ((((uint64_t)x) << 56) ) | \ | |
((((uint64_t)x) << 40) & 0x00ff000000000000ULL ) | \ | |
((((uint64_t)x) << 24) & 0x0000ff0000000000ULL ) | \ | |
((((uint64_t)x) << 8) & 0x000000ff00000000ULL ) | \ | |
((((uint64_t)x) >> 8) & 0x00000000ff000000ULL ) | \ | |
((((uint64_t)x) >> 24) & 0x0000000000ff0000ULL ) | \ | |
((((uint64_t)x) >> 40) & 0x000000000000ff00ULL ) | \ | |
((((uint64_t)x) >> 56) ) ) | |
#endif | |
#define _load32(dst, src, type) \ | |
do { \ | |
memcpy((type*) (dst), (src), sizeof(type)); \ | |
*(dst) = ntohl(*(dst)); \ | |
} while (0); | |
#define _load64(dst, src, type) \ | |
do { \ | |
memcpy((type*) (dst), (src), sizeof(type)); \ | |
*(dst) = _ntohll(*(dst)); \ | |
} while (0); | |
#define _shift32(dst, src, type) (*(dst) = (type) ( \ | |
(((uint32_t)((uint8_t*)(src))[0]) << 24) | \ | |
(((uint32_t)((uint8_t*)(src))[1]) << 16) | \ | |
(((uint32_t)((uint8_t*)(src))[2]) << 8) | \ | |
(((uint32_t)((uint8_t*)(src))[3]) ) )) | |
#define _shift64(dst, src, type) (*(dst) = (type) ( \ | |
(((uint64_t)((uint8_t*)(src))[0]) << 56) | \ | |
(((uint64_t)((uint8_t*)(src))[1]) << 48) | \ | |
(((uint64_t)((uint8_t*)(src))[2]) << 40) | \ | |
(((uint64_t)((uint8_t*)(src))[3]) << 32) | \ | |
(((uint64_t)((uint8_t*)(src))[4]) << 24) | \ | |
(((uint64_t)((uint8_t*)(src))[5]) << 16) | \ | |
(((uint64_t)((uint8_t*)(src))[6]) << 8) | \ | |
(((uint64_t)((uint8_t*)(src))[7]) ) )) | |
#define FILE_PATH "random.data" | |
static volatile int32_t v32; | |
static volatile int64_t v64; | |
static void run_shift(const char* data, size_t size) | |
{ | |
const size_t last = size - 9; | |
for(size_t i=0; i < last; i++) { | |
char b = data[i]; | |
i++; | |
if(b < 0) { | |
_shift32(&v32, data + i, int32_t); | |
i += 4; | |
} else { | |
_shift64(&v64, data + i, int64_t); | |
i += 8; | |
} | |
} | |
} | |
static void run_load(const char* data, size_t size) | |
{ | |
const size_t last = size - 9; | |
for(size_t i=0; i < last; i++) { | |
char b = data[i]; | |
i++; | |
if(b < 0) { | |
_load32(&v32, data + i, int32_t); | |
i += 4; | |
} else { | |
_load64(&v64, data + i, int64_t); | |
i += 8; | |
} | |
} | |
} | |
static void show_measured(const char* name, | |
size_t size, int loop, | |
const struct timeval* start, const struct timeval* finish) | |
{ | |
double time = | |
(finish->tv_sec - start->tv_sec) * 1000.0 + | |
(finish->tv_usec - start->tv_usec) / 1000.0; | |
double msec = time / loop; | |
double mbs = size * loop / (time / 1000) / 1024 / 1024; | |
printf("-- %s\n", name); | |
printf(" %.2f msec/loop\n", msec); | |
printf(" %.2f MB/s\n", mbs); | |
} | |
int main(void) | |
{ | |
const int loop = LOOP; // compile with -DLOOP=N option | |
int fd = open(FILE_PATH, O_RDONLY); | |
struct stat stbuf; | |
fstat(fd, &stbuf); | |
size_t size = stbuf.st_size; | |
char* map = mmap(NULL, size, PROT_READ, | |
MAP_SHARED, fd, 0); | |
printf("size: %lu\n", size); | |
printf("loop: %u times\n", loop); | |
{ | |
// warm-up | |
for(int i=0; i < loop; i++) { | |
run_load(map, size); | |
} | |
struct timeval start; | |
gettimeofday(&start, NULL); | |
for(int i=0; i < loop; i++) { | |
run_load(map, size); | |
} | |
struct timeval finish; | |
gettimeofday(&finish, NULL); | |
show_measured("C load", size, loop, &start, &finish); | |
} | |
{ | |
// warm-up | |
for(int i=0; i < loop; i++) { | |
run_shift(map, size); | |
} | |
struct timeval start; | |
gettimeofday(&start, NULL); | |
for(int i=0; i < loop; i++) { | |
run_shift(map, size); | |
} | |
struct timeval finish; | |
gettimeofday(&finish, NULL); | |
show_measured("C shift", size, loop, &start, &finish); | |
} | |
munmap(map, size); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Random; | |
import sun.misc.Unsafe; | |
import sun.nio.ch.DirectBuffer; | |
import java.lang.reflect.Field; | |
import java.nio.ByteBuffer; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
public class DeserBenchmark { | |
public static void main(String[] args) throws Exception { | |
if (args.length != 3) { | |
System.out.println("usage: <seed> <size> <loop>"); | |
System.exit(1); | |
} | |
long seed = Long.parseLong(args[0]); | |
int size = Integer.parseInt(args[1]); | |
int loop = Integer.parseInt(args[2]); | |
new DeserBenchmark().run(seed, size, loop); | |
} | |
public void run(long seed, int size, int loop) { | |
if (seed == 0) { | |
seed = new Random().nextInt(); | |
} | |
System.out.printf("seed: %d%n", seed); | |
System.out.printf("size: %d bytes%n", size); | |
System.out.printf("loop: %d times%n", loop); | |
byte[] bytes = new byte[size]; | |
new Random(seed).nextBytes(bytes); | |
ByteBuffer heap = ByteBuffer.wrap(bytes); | |
ByteBuffer direct = ByteBuffer.allocateDirect(size); | |
direct.put(bytes); | |
measure("ByteBuffer heap", size, loop, new ByteBufferRunnable(heap)); | |
measure("ByteBuffer direct", size, loop, new ByteBufferRunnable(direct)); | |
measure("Unsafe abstract heap", size, loop, new HeapUnsafeAbstractRunnable(bytes)); | |
measure("Unsafe abstract direct", size, loop, new DirectUnsafeAbstractRunnable(direct)); | |
measure("Unsafe heap", size, loop, new HeapUnsafeRunnable(bytes)); | |
measure("Unsafe direct", size, loop, new DirectUnsafeRunnable(direct)); | |
System.out.println("writing data to random.data file"); | |
try { | |
new FileOutputStream(new File("random.data")).write(bytes); | |
} catch (IOException ex) { | |
ex.printStackTrace(); | |
} | |
} | |
private void measure(String name, int size, int loop, Runnable runnable) { | |
// warm-up | |
for (int i=0; i < loop; i++) { | |
runnable.run(); | |
} | |
long startTime = System.currentTimeMillis(); | |
for (int i=0; i < loop; i++) { | |
runnable.run(); | |
} | |
long time = System.currentTimeMillis() - startTime; | |
double mbs = size * (long) loop / ((double) time / 1000) / 1024 / 1024; | |
double msec = time / (double) loop; | |
System.out.printf("-- %s%n", name); | |
System.out.printf(" %.2f msec/loop%n", msec); | |
System.out.printf(" %.2f MB/s%n", mbs); | |
} | |
private static class ByteBufferRunnable implements Runnable { | |
private ByteBuffer src; | |
public int v32; | |
public long v64; | |
public ByteBufferRunnable(ByteBuffer src) { | |
this.src = src; | |
} | |
public void run() { | |
int last = src.limit() - 9; | |
for(int i=0; i < last; i++) { | |
byte b = src.get(i); | |
i++; | |
if(b < 0) { | |
v32 = src.getInt(i); | |
i += 4; | |
} else { | |
v64 = src.getLong(i); | |
i += 8; | |
} | |
} | |
} | |
} | |
private static class UnsafeRunnable implements Runnable { | |
private static final Unsafe unsafe; | |
public int v32; | |
public long v64; | |
static { | |
try { | |
Field field = Unsafe.class.getDeclaredField("theUnsafe"); | |
field.setAccessible(true); | |
unsafe = (Unsafe) field.get(null); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
private Object base; | |
private long address; | |
private int length; | |
public UnsafeRunnable(Object base, long address, int length) { | |
this.base = base; | |
this.length = length; | |
} | |
public void run() { | |
int last = length - 9; | |
for(int i=0; i < last; i++) { | |
byte b = unsafe.getByte(base, address + i); | |
i++; | |
if(b < 0) { | |
v32 = unsafe.getInt(base, address + i); | |
i += 4; | |
} else { | |
v64 = unsafe.getLong(base, address + i); | |
i += 8; | |
} | |
} | |
} | |
} | |
private static class HeapUnsafeRunnable extends UnsafeRunnable { | |
public HeapUnsafeRunnable(byte[] src) { | |
super(src, Unsafe.ARRAY_BYTE_BASE_OFFSET, src.length); | |
} | |
} | |
private static class DirectUnsafeRunnable extends UnsafeRunnable { | |
public DirectUnsafeRunnable(ByteBuffer src) { | |
super(src, ((DirectBuffer) src).address(), src.limit()); | |
} | |
} | |
private static abstract class UnsafeAbstractRunnable implements Runnable { | |
protected static final Unsafe unsafe; | |
public int v32; | |
public long v64; | |
static { | |
try { | |
Field field = Unsafe.class.getDeclaredField("theUnsafe"); | |
field.setAccessible(true); | |
unsafe = (Unsafe) field.get(null); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
protected Object base; | |
protected long address; | |
protected int length; | |
public UnsafeAbstractRunnable(Object base, long address, int length) { | |
this.base = base; | |
this.length = length; | |
} | |
public void run() { | |
int last = length - 9; | |
for(int i=0; i < last; i++) { | |
byte b = getByte(i); | |
i++; | |
if(b < 0) { | |
v32 = getInt(i); | |
i += 4; | |
} else { | |
v64 = getLong(i); | |
i += 8; | |
} | |
} | |
} | |
public abstract byte getByte(int i); | |
public abstract int getInt(int i); | |
public abstract long getLong(int i); | |
} | |
private static class HeapUnsafeAbstractRunnable extends UnsafeAbstractRunnable { | |
public HeapUnsafeAbstractRunnable(byte[] src) { | |
super(src, Unsafe.ARRAY_BYTE_BASE_OFFSET, src.length); | |
} | |
@Override | |
public byte getByte(int i) { | |
return unsafe.getByte(base, address + i); | |
} | |
@Override | |
public int getInt(int i) { | |
return unsafe.getByte(base, address + i); | |
} | |
@Override | |
public long getLong(int i) { | |
return unsafe.getByte(base, address + i); | |
} | |
} | |
private static class DirectUnsafeAbstractRunnable extends UnsafeAbstractRunnable { | |
public DirectUnsafeAbstractRunnable(ByteBuffer src) { | |
super(src, ((DirectBuffer) src).address(), src.limit()); | |
} | |
@Override | |
public byte getByte(int i) { | |
return unsafe.getByte(base, address + i); | |
} | |
@Override | |
public int getInt(int i) { | |
return unsafe.getByte(base, address + i); | |
} | |
@Override | |
public long getLong(int i) { | |
return unsafe.getByte(base, address + i); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: run | |
DeserBenchmark.class: DeserBenchmark.java | |
javac DeserBenchmark.java | |
deser_benchmark: deser_benchmark.c | |
gcc -Wall deser_benchmark.c --std=c99 -O2 -DLOOP=1000 -o deser_benchmark | |
run: DeserBenchmark.class deser_benchmark | |
java -cp . DeserBenchmark 1700461846 10000000 1000 | |
./deser_benchmark | |
clean: | |
rm -f *.class | |
rm -f random.data | |
rm -f deser_benchmark | |
.PHONY: clean run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TODO |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment