if (o->compress_percentage || o->dedupe_percentage) {
unsigned int perc = td->o.compress_percentage;
- struct frand_state *rs;
+ struct frand_state *rs = NULL;
unsigned long long left = max_bs;
unsigned long long this_write;
do {
- rs = get_buf_state(td);
+ /*
+ * Buffers are either entirely dedupe-able or not.
+ * If we choose to dedup, the buffer should undergo
+ * the same manipulation as the original write. Which
+ * means we should retrack the steps we took for compression
+ * as well.
+ */
+ if (!rs)
+ rs = get_buf_state(td);
min_write = min(min_write, left);
- if (perc) {
- this_write = min_not_zero(min_write,
- (unsigned long long) td->o.compress_chunk);
+ this_write = min_not_zero(min_write,
+ (unsigned long long) td->o.compress_chunk);
- fill_random_buf_percentage(rs, buf, perc,
- this_write, this_write,
- o->buffer_pattern,
- o->buffer_pattern_bytes);
- } else {
- fill_random_buf(rs, buf, min_write);
- this_write = min_write;
- }
+ fill_random_buf_percentage(rs, buf, perc,
+ this_write, this_write,
+ o->buffer_pattern,
+ o->buffer_pattern_bytes);
buf += this_write;
left -= this_write;
#include "../lib/bloom.h"
#include "debug.h"
+#include "zlib.h"
+
+struct zlib_ctrl {
+ z_stream stream;
+ unsigned char *buf_in;
+ unsigned char *buf_out;
+};
struct worker_thread {
+ struct zlib_ctrl zc;
pthread_t thread;
-
- volatile int done;
-
- int fd;
uint64_t cur_offset;
uint64_t size;
-
+ unsigned long long unique_capacity;
unsigned long items;
unsigned long dupes;
int err;
+ int fd;
+ volatile int done;
};
struct extent {
static unsigned int collision_check;
static unsigned int print_progress = 1;
static unsigned int use_bloom = 1;
+static unsigned int compression = 0;
static uint64_t total_size;
static uint64_t cur_offset;
return __read_block(fd, buf, offset, blocksize);
}
+static void account_unique_capacity(uint64_t offset, uint64_t *unique_capacity, struct zlib_ctrl *zc)
+{
+ z_stream *stream = &zc->stream;
+ unsigned int compressed_len;
+ int ret;
+
+ if (read_block(file.fd, zc->buf_in, offset))
+ return;
+
+ stream->next_in = zc->buf_in;
+ stream->avail_in = blocksize;
+ stream->avail_out = deflateBound(stream, blocksize);
+ stream->next_out = zc->buf_out;
+
+ ret = deflate(stream, Z_FINISH);
+ assert(ret != Z_STREAM_ERROR);
+ compressed_len = blocksize - stream->avail_out;
+
+ if (dump_output)
+ printf("offset 0x%lx compressed to %d blocksize %d ratio %.2f \n",
+ (unsigned long) offset, compressed_len, blocksize,
+ (float)compressed_len / (float)blocksize);
+
+ *unique_capacity += compressed_len;
+
+ deflateReset(stream);
+}
+
static void add_item(struct chunk *c, struct item *i)
{
/*
return c;
}
-static void insert_chunk(struct item *i)
+static void insert_chunk(struct item *i, uint64_t *unique_capacity, struct zlib_ctrl *zc)
{
struct fio_rb_node **p, *parent;
struct chunk *c;
memcpy(c->hash, i->hash, sizeof(i->hash));
rb_link_node(&c->rb_node, parent, p);
rb_insert_color(&c->rb_node, &rb_root);
+ if (compression)
+ account_unique_capacity(i->offset, unique_capacity, zc);
add:
add_item(c, i);
}
static void insert_chunks(struct item *items, unsigned int nitems,
- uint64_t *ndupes)
+ uint64_t *ndupes, uint64_t *unique_capacity, struct zlib_ctrl *zc)
{
int i;
r = bloom_set(bloom, items[i].hash, s);
*ndupes += r;
} else
- insert_chunk(&items[i]);
+ insert_chunk(&items[i], unique_capacity, zc);
}
fio_sem_up(rb_lock);
off_t offset;
int nitems = 0;
uint64_t ndupes = 0;
+ uint64_t unique_capacity = 0;
struct item *items;
offset = thread->cur_offset;
nitems++;
}
- insert_chunks(items, nitems, &ndupes);
+ insert_chunks(items, nitems, &ndupes, &unique_capacity, &thread->zc);
free(items);
thread->items += nitems;
thread->dupes += ndupes;
+ thread->unique_capacity += unique_capacity;
return 0;
}
+static void thread_init_zlib_control(struct worker_thread *thread)
+{
+ z_stream *stream = &thread->zc.stream;
+ stream->zalloc = Z_NULL;
+ stream->zfree = Z_NULL;
+ stream->opaque = Z_NULL;
+
+ if (deflateInit(stream, Z_DEFAULT_COMPRESSION) != Z_OK)
+ return;
+
+ thread->zc.buf_in = fio_memalign(blocksize, blocksize, false);
+ thread->zc.buf_out = fio_memalign(blocksize, deflateBound(stream, blocksize), false);
+}
+
static void *thread_fn(void *data)
{
struct worker_thread *thread = data;
buf = fio_memalign(blocksize, chunk_size, false);
+ thread_init_zlib_control(thread);
+
do {
if (get_work(&thread->cur_offset, &thread->size)) {
thread->err = 1;
}
static int run_dedupe_threads(struct fio_file *f, uint64_t dev_size,
- uint64_t *nextents, uint64_t *nchunks)
+ uint64_t *nextents, uint64_t *nchunks, uint64_t *unique_capacity)
{
struct worker_thread *threads;
unsigned long nitems, total_items;
nitems = 0;
*nextents = 0;
*nchunks = 1;
+ *unique_capacity = 0;
for (i = 0; i < num_threads; i++) {
void *ret;
pthread_join(threads[i].thread, &ret);
nitems += threads[i].items;
*nchunks += threads[i].dupes;
+ *unique_capacity += threads[i].unique_capacity;
}
printf("Threads(%u): %lu items processed\n", num_threads, nitems);
}
static int dedupe_check(const char *filename, uint64_t *nextents,
- uint64_t *nchunks)
+ uint64_t *nchunks, uint64_t *unique_capacity)
{
uint64_t dev_size;
struct stat sb;
printf("Will check <%s>, size <%llu>, using %u threads\n", filename, (unsigned long long) dev_size, num_threads);
- return run_dedupe_threads(&file, dev_size, nextents, nchunks);
+ return run_dedupe_threads(&file, dev_size, nextents, nchunks, unique_capacity);
err:
if (file.fd != -1)
close(file.fd);
}
}
-static void show_stat(uint64_t nextents, uint64_t nchunks, uint64_t ndupextents)
+static const char *capacity_unit[] = {"b","KB", "MB", "GB", "TB", "PB", "EB"};
+
+static uint64_t bytes_to_human_readable_unit(uint64_t n, const char **unit_out)
+{
+ uint8_t i = 0;
+
+ while (n >= 1024) {
+ i++;
+ n /= 1024;
+ }
+
+ *unit_out = capacity_unit[i];
+
+ return n;
+}
+
+static void show_stat(uint64_t nextents, uint64_t nchunks, uint64_t ndupextents, uint64_t unique_capacity)
{
double perc, ratio;
+ const char *unit;
+ uint64_t uc_human;
printf("Extents=%lu, Unique extents=%lu", (unsigned long) nextents, (unsigned long) nchunks);
if (!bloom)
perc *= 100.0;
printf("Fio setting: dedupe_percentage=%u\n", (int) (perc + 0.50));
+
+ if (compression) {
+ uc_human = bytes_to_human_readable_unit(unique_capacity, &unit);
+ printf("Unique capacity %lu%s\n", (unsigned long) uc_human, unit);
+ }
}
static void iter_rb_tree(uint64_t *nextents, uint64_t *nchunks, uint64_t *ndupextents)
{
struct fio_rb_node *n;
-
*nchunks = *nextents = *ndupextents = 0;
n = rb_first(&rb_root);
log_err("\t-c\tFull collision check\n");
log_err("\t-B\tUse probabilistic bloom filter\n");
log_err("\t-p\tPrint progress indicator\n");
+ log_err("\t-C\tCalculate compressible size\n");
return 1;
}
int main(int argc, char *argv[])
{
- uint64_t nextents = 0, nchunks = 0, ndupextents = 0;
+ uint64_t nextents = 0, nchunks = 0, ndupextents = 0, unique_capacity;
int c, ret;
arch_init(argv);
debug_init();
- while ((c = getopt(argc, argv, "b:t:d:o:c:p:B:")) != -1) {
+ while ((c = getopt(argc, argv, "b:t:d:o:c:p:B:C:")) != -1) {
switch (c) {
case 'b':
blocksize = atoi(optarg);
case 'B':
use_bloom = atoi(optarg);
break;
+ case 'C':
+ compression = atoi(optarg);
+ break;
case '?':
default:
return usage(argv);
}
}
- if (collision_check || dump_output)
+ if (collision_check || dump_output || compression)
use_bloom = 0;
if (!num_threads)
rb_root = RB_ROOT;
rb_lock = fio_sem_init(FIO_SEM_UNLOCKED);
- ret = dedupe_check(argv[optind], &nextents, &nchunks);
+ ret = dedupe_check(argv[optind], &nextents, &nchunks, &unique_capacity);
if (!ret) {
if (!bloom)
iter_rb_tree(&nextents, &nchunks, &ndupextents);
- show_stat(nextents, nchunks, ndupextents);
+ show_stat(nextents, nchunks, ndupextents, unique_capacity);
}
fio_sem_remove(rb_lock);