Skip to content

Commit 7cd136a

Browse files
jamieprydeaainscow
authored andcommitted
erasure-code: Add parity delta write optimization to ISA
This commit adds FLAG_EC_PLUGIN_PARITY_DELTA_OPTIMIZATION to the list of optimizations supported by the ISA plugin, and encode_delta and apply_delta functions to ISA. Stubs for these functions are added to the other plugins. Two new tests have been added to TestErasureCodePlugins.cc to test the encode_delta and apply_delta functions. The first new test updates a single parity chunk using a single delta. The second new test updates all parity chunks with a delta for every data chunk. Signed-off-by: Jamie Pryde <[email protected]> Signed-off-by: Alex Ainscow <[email protected]>
1 parent f3753fd commit 7cd136a

File tree

5 files changed

+410
-103
lines changed

5 files changed

+410
-103
lines changed

src/erasure-code/ErasureCode.h

Lines changed: 90 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -20,111 +20,125 @@
2020
/*! @file ErasureCode.h
2121
@brief Base class for erasure code plugins implementors
2222
23-
*/
23+
*/
2424

2525
#include "ErasureCodeInterface.h"
26+
#include "include/ceph_assert.h"
2627

2728
namespace ceph {
2829

29-
class ErasureCode : public ErasureCodeInterface {
30-
public:
31-
static const unsigned SIMD_ALIGN;
30+
class ErasureCode : public ErasureCodeInterface {
31+
public:
32+
static const unsigned SIMD_ALIGN;
33+
34+
std::vector<int> chunk_mapping;
35+
ErasureCodeProfile _profile;
3236

33-
std::vector<int> chunk_mapping;
34-
ErasureCodeProfile _profile;
37+
// for CRUSH rule
38+
std::string rule_root;
39+
std::string rule_failure_domain;
40+
std::string rule_device_class;
41+
int rule_osds_per_failure_domain = -1;
42+
int rule_num_failure_domains = -1;
3543

36-
// for CRUSH rule
37-
std::string rule_root;
38-
std::string rule_failure_domain;
39-
std::string rule_device_class;
40-
int rule_osds_per_failure_domain = -1;
41-
int rule_num_failure_domains = -1;
44+
~ErasureCode() override {}
4245

43-
~ErasureCode() override {}
46+
int init(ceph::ErasureCodeProfile &profile, std::ostream *ss) override;
4447

45-
int init(ceph::ErasureCodeProfile &profile, std::ostream *ss) override;
48+
const ErasureCodeProfile &get_profile() const override {
49+
return _profile;
50+
}
4651

47-
const ErasureCodeProfile &get_profile() const override {
48-
return _profile;
49-
}
52+
int create_rule(const std::string &name,
53+
CrushWrapper &crush,
54+
std::ostream *ss) const override;
5055

51-
int create_rule(const std::string &name,
52-
CrushWrapper &crush,
53-
std::ostream *ss) const override;
56+
int sanity_check_k_m(int k, int m, std::ostream *ss);
5457

55-
int sanity_check_k_m(int k, int m, std::ostream *ss);
58+
unsigned int get_coding_chunk_count() const override {
59+
return get_chunk_count() - get_data_chunk_count();
60+
}
5661

57-
unsigned int get_coding_chunk_count() const override {
58-
return get_chunk_count() - get_data_chunk_count();
59-
}
62+
virtual int get_sub_chunk_count() override {
63+
return 1;
64+
}
6065

61-
virtual int get_sub_chunk_count() override {
62-
return 1;
63-
}
66+
virtual int _minimum_to_decode(const std::set<int> &want_to_read,
67+
const std::set<int> &available_chunks,
68+
std::set<int> *minimum);
6469

65-
virtual int _minimum_to_decode(const std::set<int> &want_to_read,
66-
const std::set<int> &available_chunks,
67-
std::set<int> *minimum);
70+
int minimum_to_decode(const std::set<int> &want_to_read,
71+
const std::set<int> &available,
72+
std::map<int, std::vector<std::pair<int, int>>> *minimum) override;
6873

69-
int minimum_to_decode(const std::set<int> &want_to_read,
70-
const std::set<int> &available,
71-
std::map<int, std::vector<std::pair<int, int>>> *minimum) override;
74+
int minimum_to_decode_with_cost(const std::set<int> &want_to_read,
75+
const std::map<int, int> &available,
76+
std::set<int> *minimum) override;
7277

73-
int minimum_to_decode_with_cost(const std::set<int> &want_to_read,
74-
const std::map<int, int> &available,
75-
std::set<int> *minimum) override;
78+
int encode_prepare(const bufferlist &raw,
79+
std::map<int, bufferlist> &encoded) const;
7680

77-
int encode_prepare(const bufferlist &raw,
78-
std::map<int, bufferlist> &encoded) const;
81+
int encode(const std::set<int> &want_to_encode,
82+
const bufferlist &in,
83+
std::map<int, bufferlist> *encoded) override;
7984

80-
int encode(const std::set<int> &want_to_encode,
81-
const bufferlist &in,
82-
std::map<int, bufferlist> *encoded) override;
85+
int decode(const std::set<int> &want_to_read,
86+
const std::map<int, bufferlist> &chunks,
87+
std::map<int, bufferlist> *decoded, int chunk_size) override;
8388

84-
int decode(const std::set<int> &want_to_read,
85-
const std::map<int, bufferlist> &chunks,
86-
std::map<int, bufferlist> *decoded, int chunk_size) override;
89+
virtual int _decode(const std::set<int> &want_to_read,
90+
const std::map<int, bufferlist> &chunks,
91+
std::map<int, bufferlist> *decoded);
8792

88-
virtual int _decode(const std::set<int> &want_to_read,
89-
const std::map<int, bufferlist> &chunks,
90-
std::map<int, bufferlist> *decoded);
93+
const std::vector<int> &get_chunk_mapping() const override;
9194

92-
const std::vector<int> &get_chunk_mapping() const override;
95+
int to_mapping(const ErasureCodeProfile &profile,
96+
std::ostream *ss);
9397

94-
int to_mapping(const ErasureCodeProfile &profile,
95-
std::ostream *ss);
98+
static int to_int(const std::string &name,
99+
ErasureCodeProfile &profile,
100+
int *value,
101+
const std::string &default_value,
102+
std::ostream *ss);
96103

97-
static int to_int(const std::string &name,
98-
ErasureCodeProfile &profile,
99-
int *value,
100-
const std::string &default_value,
101-
std::ostream *ss);
104+
static int to_bool(const std::string &name,
105+
ErasureCodeProfile &profile,
106+
bool *value,
107+
const std::string &default_value,
108+
std::ostream *ss);
102109

103-
static int to_bool(const std::string &name,
110+
static int to_string(const std::string &name,
104111
ErasureCodeProfile &profile,
105-
bool *value,
112+
std::string *value,
106113
const std::string &default_value,
107114
std::ostream *ss);
108115

109-
static int to_string(const std::string &name,
110-
ErasureCodeProfile &profile,
111-
std::string *value,
112-
const std::string &default_value,
113-
std::ostream *ss);
114-
115-
int decode_concat(const std::set<int>& want_to_read,
116-
const std::map<int, bufferlist> &chunks,
117-
bufferlist *decoded) override;
118-
int decode_concat(const std::map<int, bufferlist> &chunks,
119-
bufferlist *decoded) override;
120-
121-
protected:
122-
int parse(const ErasureCodeProfile &profile,
123-
std::ostream *ss);
124-
125-
private:
126-
int chunk_index(unsigned int i) const;
127-
};
116+
int decode_concat(const std::set<int>& want_to_read,
117+
const std::map<int, bufferlist> &chunks,
118+
bufferlist *decoded) override;
119+
int decode_concat(const std::map<int, bufferlist> &chunks,
120+
bufferlist *decoded) override;
121+
122+
void encode_delta(const bufferptr &old_data,
123+
const bufferptr &new_data,
124+
bufferptr *delta_maybe_in_place) override
125+
{
126+
ceph_abort("Not yet supported by this plugin");
127+
}
128+
129+
void apply_delta(const shard_id_map<bufferptr> &in,
130+
shard_id_map<bufferptr> &out) override
131+
{
132+
ceph_abort("Not yet supported by this plugin");
133+
}
134+
135+
protected:
136+
int parse(const ErasureCodeProfile &profile,
137+
std::ostream *ss);
138+
139+
private:
140+
int chunk_index(unsigned int i) const;
141+
};
128142
}
129143

130144
#endif

src/erasure-code/ErasureCodeInterface.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,56 @@ namespace ceph {
379379
virtual int encode_chunks(const std::set<int> &want_to_encode,
380380
std::map<int, bufferlist> *encoded) = 0;
381381

382+
/**
383+
* Calculate the delta between the old_data and new_data buffers using xor,
384+
* (or plugin-specific implementation) and returns the result in the
385+
* delta_maybe_in_place buffer.
386+
*
387+
* Assumes old_data, new_data and delta_maybe_in_place are all buffers of
388+
* the same length.
389+
*
390+
* Optionally, the delta_maybe_in_place and old_data parameters can be the
391+
* same buffer. For some plugins making these the same buffer is slightly
392+
* faster, as it avoids a memcpy. Reduced allocations in the caller may
393+
* also provide a performance advantage.
394+
*
395+
* @param [in] old_data first buffer to xor
396+
* @param [in] new_data second buffer to xor
397+
* @delta_maybe_in_place [out] delta buffer to write the delta of
398+
* old_data and new_data. This can optionally be a
399+
* pointer to old_data.
400+
*/
401+
virtual void encode_delta(const bufferptr &old_data,
402+
const bufferptr &new_data,
403+
bufferptr *delta_maybe_in_place) = 0;
404+
405+
/**
406+
* Applies one or more deltas to one or more coding
407+
* chunks.
408+
*
409+
* Assumes all buffers in the in and out maps are the same length.
410+
*
411+
* The in map should contain deltas of data chunks to be applied to
412+
* the coding chunks. The delta for a specific data chunk must have
413+
* the correct integer key in the map. e.g. if k=2 m=2 and a delta for k[1]
414+
* is being applied, then the delta should have key 1 in the in map.
415+
*
416+
* The in map should also contain the coding chunks that the delta will
417+
* be applied to. The coding chunks must also have the correct integer key in the
418+
* map. e.g. if k=2 m=2 and the delta for k[1] is to be applied to m[1], then
419+
* the coding chunk should have key 3 in the in map.
420+
*
421+
* If a coding buffer is present in the in map, then it must also be present in the
422+
* out map with the same key.
423+
*
424+
*
425+
* @param [in] old_data first buffer to xor
426+
* @param [in] new_data second buffer to xor
427+
* @param [out] delta buffer containing the delta of old_data and new_data
428+
*/
429+
virtual void apply_delta(const std::map<int, bufferptr> &in,
430+
std::map <int, bufferptr> &out) = 0;
431+
382432
/**
383433
* Decode the **chunks** and store at least **want_to_read**
384434
* chunks in **decoded**.

src/erasure-code/isa/ErasureCodeIsa.cc

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -117,33 +117,40 @@ int ErasureCodeIsa::decode_chunks(const set<int> &want_to_read,
117117
// -----------------------------------------------------------------------------
118118

119119
void
120-
ErasureCodeIsa::isa_xor(char **data, char **coding, int blocksize)
120+
ErasureCodeIsa::isa_xor(char **data, char *coding, int blocksize, int data_vectors)
121121
{
122-
// If addresses are aligned to 32 bytes, then we can use xor_gen()
123-
// Otherwise, use byte_xor()
124-
int i;
125-
bool src_aligned = true;
122+
ceph_assert(data_vectors <= MAX_K);
123+
char * xor_bufs[MAX_K + 1];
124+
for (int i = 0; i < data_vectors; i++) {
125+
xor_bufs[i] = data[i];
126+
}
127+
xor_bufs[data_vectors] = coding;
126128

127-
for (i = 0; i < k; i++) {
128-
src_aligned &= is_aligned(data[i], EC_ISA_ADDRESS_ALIGNMENT);
129-
}
129+
// If addresses are aligned to 32 bytes, then we can use xor_gen()
130+
// Otherwise, use byte_xor()
131+
bool aligned = true;
132+
for (int i = 0; i <= data_vectors; i++) {
133+
aligned &= is_aligned(xor_bufs[i], EC_ISA_ADDRESS_ALIGNMENT);
134+
}
130135

131-
if (src_aligned && is_aligned(coding[0], EC_ISA_ADDRESS_ALIGNMENT)) {
132-
xor_gen(k+1, blocksize, (void**) data);
133-
}
134-
else {
135-
memcpy(coding[0], data[0], blocksize);
136-
for (i = 1; i < k; i++) {
137-
byte_xor(data[i], coding[0], data[i]+blocksize);
138-
}
139-
}
136+
if (aligned) {
137+
xor_gen(data_vectors + 1, blocksize, (void**) xor_bufs);
138+
}
139+
else {
140+
byte_xor(data_vectors, blocksize, xor_bufs);
141+
}
140142
}
141143

142144
void
143-
ErasureCodeIsa::byte_xor(char *data, char *coding, char *data_end)
145+
ErasureCodeIsa::byte_xor(int data_vects, int blocksize, char **array)
144146
{
145-
while (data < data_end)
146-
*coding++ ^= *data++;
147+
for (int i = 0; i < blocksize; i++) {
148+
char parity = array[0][i];
149+
for (int j = 1; j < data_vects; j++ ) {
150+
parity ^= array[j][i];
151+
}
152+
array[data_vects][i] = parity;
153+
}
147154
}
148155

149156
// -----------------------------------------------------------------------------
@@ -154,7 +161,7 @@ ErasureCodeIsaDefault::isa_encode(char **data,
154161
int blocksize)
155162
{
156163
if (m == 1) {
157-
isa_xor(data, coding, blocksize);
164+
isa_xor(data, coding[0], blocksize, k);
158165
} else {
159166
ec_encode_data(blocksize, k, m, encode_tbls,
160167
(unsigned char**) data, (unsigned char**) coding);
@@ -175,6 +182,52 @@ ErasureCodeIsaDefault::erasure_contains(int *erasures, int i)
175182

176183
// -----------------------------------------------------------------------------
177184

185+
void
186+
ErasureCodeIsaDefault::encode_delta(const bufferptr &old_data,
187+
const bufferptr &new_data,
188+
bufferptr *delta)
189+
{
190+
constexpr int data_vectors = 2;
191+
char * data[data_vectors];
192+
data[0] = const_cast<char*>(old_data.c_str());
193+
data[1] = const_cast<char*>(new_data.c_str());
194+
char * coding = delta->c_str();
195+
196+
isa_xor(data, coding, delta->length(), data_vectors);
197+
}
198+
199+
// -----------------------------------------------------------------------------
200+
201+
void
202+
ErasureCodeIsaDefault::apply_delta(const shard_id_map<bufferptr> &in,
203+
shard_id_map<bufferptr> &out)
204+
{
205+
auto first = in.begin();
206+
const unsigned blocksize = first->second.length();
207+
208+
for (auto const& [datashard, databuf] : in) {
209+
if (datashard < k) {
210+
for (auto const& [codingshard, codingbuf] : out) {
211+
if (codingshard >= k) {
212+
ceph_assert(codingbuf.length() == blocksize);
213+
if (m==1) {
214+
constexpr int data_vectors = 2;
215+
char * data[data_vectors];
216+
data[0] = const_cast<char*>(databuf.c_str());
217+
data[1] = const_cast<char*>(codingbuf.c_str());
218+
char * coding = const_cast<char*>(codingbuf.c_str());
219+
isa_xor(data, coding, blocksize, data_vectors);
220+
}
221+
else {
222+
unsigned char* data = reinterpret_cast<unsigned char*>(const_cast<char*>(databuf.c_str()));
223+
unsigned char* coding = reinterpret_cast<unsigned char*>(const_cast<char*>(codingbuf.c_str()));
224+
ec_encode_data_update(blocksize, k, 1, static_cast<int>(datashard), encode_tbls + (32 * k * (static_cast<int>(codingshard) - k)), data, &coding);
225+
}
226+
}
227+
}
228+
}
229+
}
230+
}
178231

179232

180233
// -----------------------------------------------------------------------------
@@ -262,7 +315,7 @@ ErasureCodeIsaDefault::isa_decode(int *erasures,
262315
((matrixtype == kVandermonde) && (nerrs == 1) && (erasures[0] < (k + 1)))) {
263316
// single parity decoding
264317
dout(20) << "isa_decode: reconstruct using xor_gen [" << erasures[0] << "]" << dendl;
265-
isa_xor(recover_buf, &recover_buf[k], blocksize);
318+
isa_xor(recover_buf, recover_buf[k], blocksize, k);
266319
return 0;
267320
}
268321

0 commit comments

Comments
 (0)