linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/3] Btrfs: populate heuristic with detection logic
@ 2017-07-29 13:36 Timofey Titovets
  2017-07-29 13:36 ` [PATCH v3 1/3] Btrfs: heuristic add simple sampling logic Timofey Titovets
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Timofey Titovets @ 2017-07-29 13:36 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Timofey Titovets

Based on kdave for-next
As heuristic skeleton already merged
Populate heuristic with basic code.

First patch: add simple sampling code
It's get 16 byte samples with 256 bytes shifts
over input data. Collect info about how many
different bytes (symbols) has been found in sample data

Second patch: add code for calculate
how many unique bytes has been
found in sample data
That can fast detect easy compressible data

Third patch: add code for calculate byte core set size
i.e. how many unique bytes use 90% of sample data
That code require that numbers in bucket must be sorted
That can detect easy compressible data with many repeated bytes
That can detect not compressible data with evenly distributed bytes

Changes v1 -> v2:
  - Change input data iterator shift 512 -> 256
  - Replace magic macro numbers with direct values
  - Drop useless symbol population in bucket
    as no one care about where and what symbol stored
    in bucket at now

Changes v2 -> v3 (only update #3 patch):
  - Fix u64 division problem by use u32 for input_size
  - Fix input size calculation start - end -> end - start
  - Add missing sort.h header

Timofey Titovets (3):
  Btrfs: heuristic add simple sampling logic
  Btrfs: heuristic add byte set calculation
  Btrfs: heuristic add byte core set calculation

 fs/btrfs/compression.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++-
 fs/btrfs/compression.h |  13 ++++++
 2 files changed, 120 insertions(+), 2 deletions(-)

--
2.13.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v3 1/3] Btrfs: heuristic add simple sampling logic
  2017-07-29 13:36 [PATCH v3 0/3] Btrfs: populate heuristic with detection logic Timofey Titovets
@ 2017-07-29 13:36 ` Timofey Titovets
  2017-07-29 13:36 ` [PATCH v3 2/3] Btrfs: heuristic add byte set calculation Timofey Titovets
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Timofey Titovets @ 2017-07-29 13:36 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Timofey Titovets

Get small sample from input data and calculate
byte type count for that sample into bucket.
Bucket will store info about which bytes
and how many has been detected in sample

Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
---
 fs/btrfs/compression.c | 24 ++++++++++++++++++++++--
 fs/btrfs/compression.h | 10 ++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 63f54bd2d5bb..ca7cfaad6e2f 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -1068,15 +1068,35 @@ int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end)
 	u64 index = start >> PAGE_SHIFT;
 	u64 end_index = end >> PAGE_SHIFT;
 	struct page *page;
-	int ret = 1;
+	struct heuristic_bucket_item *bucket;
+	int a, b, ret;
+	u8 symbol, *input_data;
+
+	ret = 1;
+
+	bucket = kcalloc(BTRFS_HEURISTIC_BUCKET_SIZE,
+		sizeof(struct heuristic_bucket_item), GFP_NOFS);
+
+	if (!bucket)
+		goto out;

 	while (index <= end_index) {
 		page = find_get_page(inode->i_mapping, index);
-		kmap(page);
+		input_data = kmap(page);
+		a = 0;
+		while (a < PAGE_SIZE) {
+			for (b = 0; b < BTRFS_HEURISTIC_READ_SIZE; b++) {
+				symbol = input_data[a+b];
+				bucket[symbol].count++;
+			}
+			a += BTRFS_HEURISTIC_ITER_OFFSET;
+		}
 		kunmap(page);
 		put_page(page);
 		index++;
 	}

+out:
+	kfree(bucket);
 	return ret;
 }
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
index d1f4eee2d0af..e30a9df1937e 100644
--- a/fs/btrfs/compression.h
+++ b/fs/btrfs/compression.h
@@ -129,6 +129,16 @@ struct btrfs_compress_op {
 extern const struct btrfs_compress_op btrfs_zlib_compress;
 extern const struct btrfs_compress_op btrfs_lzo_compress;

+struct heuristic_bucket_item {
+	u8  padding;
+	u8  symbol;
+	u16 count;
+};
+
+#define BTRFS_HEURISTIC_READ_SIZE 16
+#define BTRFS_HEURISTIC_ITER_OFFSET 256
+#define BTRFS_HEURISTIC_BUCKET_SIZE 256
+
 int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end);

 #endif
--
2.13.3

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3 2/3] Btrfs: heuristic add byte set calculation
  2017-07-29 13:36 [PATCH v3 0/3] Btrfs: populate heuristic with detection logic Timofey Titovets
  2017-07-29 13:36 ` [PATCH v3 1/3] Btrfs: heuristic add simple sampling logic Timofey Titovets
@ 2017-07-29 13:36 ` Timofey Titovets
  2017-07-29 13:36 ` [PATCH v3 3/3] Btrfs: heuristic add byte core " Timofey Titovets
  2017-08-17 21:52 ` [PATCH v3 0/3] Btrfs: populate heuristic with detection logic Timofey Titovets
  3 siblings, 0 replies; 5+ messages in thread
From: Timofey Titovets @ 2017-07-29 13:36 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Timofey Titovets

Calculate byte set size for data sample:
Calculate how many unique bytes has been in sample
By count all bytes in bucket with count > 0
If byte set low (~25%), data are easily compressible

Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
---
 fs/btrfs/compression.c | 27 +++++++++++++++++++++++++++
 fs/btrfs/compression.h |  1 +
 2 files changed, 28 insertions(+)

diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index ca7cfaad6e2f..1429b11f2c5f 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -1048,6 +1048,27 @@ int btrfs_decompress_buf2page(const char *buf, unsigned long buf_start,
 	return 1;
 }

+static inline int byte_set_size(const struct heuristic_bucket_item *bucket)
+{
+	int a = 0;
+	int byte_set_size = 0;
+
+	for (; a < BTRFS_HEURISTIC_BYTE_SET_THRESHOLD; a++) {
+		if (bucket[a].count > 0)
+			byte_set_size++;
+	}
+
+	for (; a < BTRFS_HEURISTIC_BUCKET_SIZE; a++) {
+		if (bucket[a].count > 0) {
+			byte_set_size++;
+			if (byte_set_size > BTRFS_HEURISTIC_BYTE_SET_THRESHOLD)
+				return byte_set_size;
+		}
+	}
+
+	return byte_set_size;
+}
+
 /*
  * Compression heuristic.
  *
@@ -1096,6 +1117,12 @@ int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end)
 		index++;
 	}

+	a = byte_set_size(bucket);
+	if (a > BTRFS_HEURISTIC_BYTE_SET_THRESHOLD) {
+		ret = 1;
+		goto out;
+	}
+
 out:
 	kfree(bucket);
 	return ret;
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
index e30a9df1937e..03857967815a 100644
--- a/fs/btrfs/compression.h
+++ b/fs/btrfs/compression.h
@@ -138,6 +138,7 @@ struct heuristic_bucket_item {
 #define BTRFS_HEURISTIC_READ_SIZE 16
 #define BTRFS_HEURISTIC_ITER_OFFSET 256
 #define BTRFS_HEURISTIC_BUCKET_SIZE 256
+#define BTRFS_HEURISTIC_BYTE_SET_THRESHOLD 64

 int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end);

--
2.13.3

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v3 3/3] Btrfs: heuristic add byte core set calculation
  2017-07-29 13:36 [PATCH v3 0/3] Btrfs: populate heuristic with detection logic Timofey Titovets
  2017-07-29 13:36 ` [PATCH v3 1/3] Btrfs: heuristic add simple sampling logic Timofey Titovets
  2017-07-29 13:36 ` [PATCH v3 2/3] Btrfs: heuristic add byte set calculation Timofey Titovets
@ 2017-07-29 13:36 ` Timofey Titovets
  2017-08-17 21:52 ` [PATCH v3 0/3] Btrfs: populate heuristic with detection logic Timofey Titovets
  3 siblings, 0 replies; 5+ messages in thread
From: Timofey Titovets @ 2017-07-29 13:36 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Timofey Titovets

Calculate byte core set for data sample:
Sort bucket's numbers in decreasing order
Count how many numbers use 90% of sample
If core set are low (<=25%), data are easily compressible
If core set high (>=80%), data are not compressible

Signed-off-by: Timofey Titovets <nefelim4ag@gmail.com>
---
 fs/btrfs/compression.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/compression.h |  2 ++
 2 files changed, 60 insertions(+)

diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 1429b11f2c5f..a469a7c21f5a 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -33,6 +33,7 @@
 #include <linux/bit_spinlock.h>
 #include <linux/slab.h>
 #include <linux/sched/mm.h>
+#include <linux/sort.h>
 #include "ctree.h"
 #include "disk-io.h"
 #include "transaction.h"
@@ -1069,6 +1070,42 @@ static inline int byte_set_size(const struct heuristic_bucket_item *bucket)
 	return byte_set_size;
 }

+/* For bucket sorting */
+static inline int heuristic_bucket_compare(const void *lv, const void *rv)
+{
+	struct heuristic_bucket_item *l = (struct heuristic_bucket_item *)(lv);
+	struct heuristic_bucket_item *r = (struct heuristic_bucket_item *)(rv);
+
+	return r->count - l->count;
+}
+
+/*
+ * Byte Core set size
+ * How many bytes use 90% of sample
+ */
+static inline int byte_core_set_size(struct heuristic_bucket_item *bucket,
+				     u32 core_set_threshold)
+{
+	int a = 0;
+	u32 coreset_sum = 0;
+
+	for (; a < BTRFS_HEURISTIC_BYTE_CORE_SET_LOW; a++)
+		coreset_sum += bucket[a].count;
+
+	if (coreset_sum > core_set_threshold)
+		return a;
+
+	for (; a < BTRFS_HEURISTIC_BYTE_CORE_SET_HIGH; a++) {
+		if (bucket[a].count == 0)
+			break;
+		coreset_sum += bucket[a].count;
+		if (coreset_sum > core_set_threshold)
+			break;
+	}
+
+	return a;
+}
+
 /*
  * Compression heuristic.
  *
@@ -1092,6 +1129,8 @@ int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end)
 	struct heuristic_bucket_item *bucket;
 	int a, b, ret;
 	u8 symbol, *input_data;
+	u32 core_set_threshold;
+	u32 input_size = end - start;

 	ret = 1;

@@ -1123,6 +1162,25 @@ int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end)
 		goto out;
 	}

+	/* Sort in reverse order */
+	sort(bucket, BTRFS_HEURISTIC_BUCKET_SIZE,
+	     sizeof(struct heuristic_bucket_item), &heuristic_bucket_compare,
+	     NULL);
+
+	core_set_threshold = (input_size*90)/(BTRFS_HEURISTIC_ITER_OFFSET*100);
+	core_set_threshold *= BTRFS_HEURISTIC_READ_SIZE;
+
+	a = byte_core_set_size(bucket, core_set_threshold);
+	if (a <= BTRFS_HEURISTIC_BYTE_CORE_SET_LOW) {
+		ret = 2;
+		goto out;
+	}
+
+	if (a >= BTRFS_HEURISTIC_BYTE_CORE_SET_HIGH) {
+		ret = 0;
+		goto out;
+	}
+
 out:
 	kfree(bucket);
 	return ret;
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
index 03857967815a..0fcd1a485adb 100644
--- a/fs/btrfs/compression.h
+++ b/fs/btrfs/compression.h
@@ -139,6 +139,8 @@ struct heuristic_bucket_item {
 #define BTRFS_HEURISTIC_ITER_OFFSET 256
 #define BTRFS_HEURISTIC_BUCKET_SIZE 256
 #define BTRFS_HEURISTIC_BYTE_SET_THRESHOLD 64
+#define BTRFS_HEURISTIC_BYTE_CORE_SET_LOW  BTRFS_HEURISTIC_BYTE_SET_THRESHOLD
+#define BTRFS_HEURISTIC_BYTE_CORE_SET_HIGH 200 // 80%

 int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end);

--
2.13.3

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v3 0/3] Btrfs: populate heuristic with detection logic
  2017-07-29 13:36 [PATCH v3 0/3] Btrfs: populate heuristic with detection logic Timofey Titovets
                   ` (2 preceding siblings ...)
  2017-07-29 13:36 ` [PATCH v3 3/3] Btrfs: heuristic add byte core " Timofey Titovets
@ 2017-08-17 21:52 ` Timofey Titovets
  3 siblings, 0 replies; 5+ messages in thread
From: Timofey Titovets @ 2017-08-17 21:52 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Timofey Titovets

2017-07-29 16:36 GMT+03:00 Timofey Titovets <nefelim4ag@gmail.com>:
> Based on kdave for-next
> As heuristic skeleton already merged
> Populate heuristic with basic code.
>
> First patch: add simple sampling code
> It's get 16 byte samples with 256 bytes shifts
> over input data. Collect info about how many
> different bytes (symbols) has been found in sample data
>
> Second patch: add code for calculate
> how many unique bytes has been
> found in sample data
> That can fast detect easy compressible data
>
> Third patch: add code for calculate byte core set size
> i.e. how many unique bytes use 90% of sample data
> That code require that numbers in bucket must be sorted
> That can detect easy compressible data with many repeated bytes
> That can detect not compressible data with evenly distributed bytes
>
> Changes v1 -> v2:
>   - Change input data iterator shift 512 -> 256
>   - Replace magic macro numbers with direct values
>   - Drop useless symbol population in bucket
>     as no one care about where and what symbol stored
>     in bucket at now
>
> Changes v2 -> v3 (only update #3 patch):
>   - Fix u64 division problem by use u32 for input_size
>   - Fix input size calculation start - end -> end - start
>   - Add missing sort.h header
>
> Timofey Titovets (3):
>   Btrfs: heuristic add simple sampling logic
>   Btrfs: heuristic add byte set calculation
>   Btrfs: heuristic add byte core set calculation
>
>  fs/btrfs/compression.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++-
>  fs/btrfs/compression.h |  13 ++++++
>  2 files changed, 120 insertions(+), 2 deletions(-)
>
> --
> 2.13.3

Hi, may be any thoughts on that patches? (i know you are busy)

---
small offtop:
I think that in future that will change:
from:
struct heuristic_bucket_item {
        u8  padding;
        u8  symbol;
        u16 count;
};

To:
struct heuristic_bucket_item {
        u32  symbol;
        u32 count;
};

This will cause some memory overhead (1024b -> 2048b (768b useless))
But that allow support *big* samples
At now max sample size 2^16-1b and heuristic usable only over 4KiB <->
1MiB-256b range (thats of course enough for 128KiB btrfs compression
window).
And that needed for aligned memory access =\
(if that needed at now of course)

Also, may be heuristic must use btrfs_compression workspaces?
I of course can't imagine performance difference on find_workspace()
vs kcalloc(), and heuristic safe to fail on memory allocation.
IMHO for using compression workspace (if i understand code correctly)
Heuristic code must move to external file (heuristic.c?) to correctly
avoid name clashes with struct workspace & etc
And may be for avoid code misunderstanding name refactoring of
workspace code needed,
because that created for compression workspaces and heuristic itself
is not compression

Thanks!

-- 
Have a nice day,
Timofey.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-08-17 21:53 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-07-29 13:36 [PATCH v3 0/3] Btrfs: populate heuristic with detection logic Timofey Titovets
2017-07-29 13:36 ` [PATCH v3 1/3] Btrfs: heuristic add simple sampling logic Timofey Titovets
2017-07-29 13:36 ` [PATCH v3 2/3] Btrfs: heuristic add byte set calculation Timofey Titovets
2017-07-29 13:36 ` [PATCH v3 3/3] Btrfs: heuristic add byte core " Timofey Titovets
2017-08-17 21:52 ` [PATCH v3 0/3] Btrfs: populate heuristic with detection logic Timofey Titovets

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).