@@ -110,6 +110,19 @@ FilterBlockBuilder* CreateFilterBlockBuilder(
110110 }
111111}
112112
113+ // A convenience function for populating the Compressor* fields; see ~Rep()
114+ Compressor* MaybeCloneSpecialized (
115+ Compressor* compressor, CacheEntryRole block_type,
116+ Compressor::DictSampleArgs&& dict_samples = {}) {
117+ auto specialized =
118+ compressor->MaybeCloneSpecialized (block_type, std::move (dict_samples));
119+ if (specialized) {
120+ // Caller is responsible for freeing when distinct
121+ return specialized.release ();
122+ } else {
123+ return compressor;
124+ }
125+ }
113126} // namespace
114127
115128// kBlockBasedTableMagicNumber was picked by running
@@ -824,15 +837,17 @@ struct BlockBasedTableBuilder::Rep {
824837
825838 // *** Compressors & decompressors - Yes, it seems like a lot here but ***
826839 // *** these are distinct fields to minimize extra conditionals and ***
827- // *** field reads on hot code paths. ***
840+ // *** field reads on hot code paths. And to avoid interlocked ***
841+ // *** instructions associated with shared_ptr. ***
828842
829843 // A compressor for blocks in general, without dictionary compression
830844 std::unique_ptr<Compressor> basic_compressor;
831- // A compressor using dictionary compression (when applicable)
832- std::unique_ptr<Compressor> compressor_with_dict;
833- // Once configured/determined, points to one of the above Compressors to
834- // use on data blocks.
835- Compressor* data_block_compressor = nullptr ;
845+ // A compressor for data blocks, which might be tuned differently and might
846+ // use dictionary compression (when applicable). See ~Rep() for some details.
847+ UnownedPtr<Compressor> data_block_compressor = nullptr ;
848+ // A compressor for index blocks, which might be tuned differently from
849+ // basic_compressor. See ~Rep() for some details.
850+ UnownedPtr<Compressor> index_block_compressor = nullptr ;
836851 // A decompressor corresponding to basic_compressor (when non-nullptr).
837852 // Used for verification and cache warming.
838853 std::shared_ptr<Decompressor> basic_decompressor;
@@ -853,7 +868,7 @@ struct BlockBasedTableBuilder::Rep {
853868 compression_types_used;
854869
855870 // Working area for basic_compressor when compression_parallel_threads==1
856- WorkingAreaPair basic_working_area ;
871+ WorkingAreaPair index_block_working_area ;
857872 // Working area for data_block_compressor, for emit/compaction thread
858873 WorkingAreaPair data_block_working_area;
859874
@@ -1099,7 +1114,10 @@ struct BlockBasedTableBuilder::Rep {
10991114 filter_context, tbo.compression_opts , tbo.compression_type );
11001115 if (basic_compressor) {
11011116 if (table_options.enable_index_compression ) {
1102- basic_working_area.compress = basic_compressor->ObtainWorkingArea ();
1117+ index_block_compressor = MaybeCloneSpecialized (
1118+ basic_compressor.get (), CacheEntryRole::kIndexBlock );
1119+ index_block_working_area.compress =
1120+ index_block_compressor->ObtainWorkingArea ();
11031121 }
11041122 max_dict_sample_bytes = basic_compressor->GetMaxSampleSizeIfWantDict (
11051123 CacheEntryRole::kDataBlock );
@@ -1114,8 +1132,10 @@ struct BlockBasedTableBuilder::Rep {
11141132 tbo.compression_opts .max_dict_buffer_bytes );
11151133 }
11161134 } else {
1117- // No distinct data block compressor using dictionary
1118- data_block_compressor = basic_compressor.get ();
1135+ // No distinct data block compressor using dictionary, but
1136+ // implementation might still want to specialize for data blocks
1137+ data_block_compressor = MaybeCloneSpecialized (
1138+ basic_compressor.get (), CacheEntryRole::kDataBlock );
11191139 data_block_working_area.compress =
11201140 data_block_compressor->ObtainWorkingArea ();
11211141 }
@@ -1129,8 +1149,9 @@ struct BlockBasedTableBuilder::Rep {
11291149 if (table_options.verify_compression ) {
11301150 verify_decompressor = basic_decompressor.get ();
11311151 if (table_options.enable_index_compression ) {
1132- basic_working_area.verify = verify_decompressor->ObtainWorkingArea (
1133- basic_compressor->GetPreferredCompressionType ());
1152+ index_block_working_area.verify =
1153+ verify_decompressor->ObtainWorkingArea (
1154+ index_block_compressor->GetPreferredCompressionType ());
11341155 }
11351156 if (state == State::kUnbuffered ) {
11361157 assert (data_block_compressor);
@@ -1295,8 +1316,19 @@ struct BlockBasedTableBuilder::Rep {
12951316 }
12961317
12971318 ~Rep () {
1319+ // Delete working areas before their compressors.
1320+ index_block_working_area = {};
1321+ data_block_working_area = {};
12981322 // Must have been cleaned up by StopParallelCompression
12991323 assert (pc_rep == nullptr );
1324+ // Delete specialized compressors if they were distinct (avoiding extra
1325+ // fields and interlocked instructions with shared_ptr)
1326+ if (data_block_compressor.get () != basic_compressor.get ()) {
1327+ delete data_block_compressor.get ();
1328+ }
1329+ if (index_block_compressor.get () != basic_compressor.get ()) {
1330+ delete index_block_compressor.get ();
1331+ }
13001332 }
13011333
13021334 Rep (const Rep&) = delete;
@@ -1729,9 +1761,11 @@ void BlockBasedTableBuilder::WriteBlock(const Slice& uncompressed_block_data,
17291761 assert (!r->IsParallelCompressionActive ());
17301762 CompressionType type;
17311763 bool is_data_block = block_type == BlockType::kData ;
1764+ // NOTE: only index and data blocks are currently compressed
1765+ assert (is_data_block || block_type == BlockType::kIndex );
17321766 Status compress_status = CompressAndVerifyBlock (
17331767 uncompressed_block_data, is_data_block,
1734- is_data_block ? r->data_block_working_area : r->basic_working_area ,
1768+ is_data_block ? r->data_block_working_area : r->index_block_working_area ,
17351769 &r->single_threaded_compressed_output , &type);
17361770 r->SetStatus (compress_status);
17371771 if (UNLIKELY (!ok ())) {
@@ -1845,13 +1879,13 @@ Status BlockBasedTableBuilder::CompressAndVerifyBlock(
18451879 Rep* r = rep_.get ();
18461880 Status status;
18471881
1848- Compressor* compressor = nullptr ;
1882+ UnownedPtr< Compressor> compressor = nullptr ;
18491883 Decompressor* verify_decomp = nullptr ;
18501884 if (is_data_block) {
18511885 compressor = r->data_block_compressor ;
18521886 verify_decomp = r->data_block_verify_decompressor .get ();
18531887 } else {
1854- compressor = r->basic_compressor . get () ;
1888+ compressor = r->index_block_compressor ;
18551889 verify_decomp = r->verify_decompressor .get ();
18561890 }
18571891
@@ -2116,7 +2150,7 @@ void BlockBasedTableBuilder::MaybeStartParallelCompression() {
21162150 // that latency. So even with some optimizations, turning on the parallel
21172151 // framework when compression is disabled just eats more CPU with little-to-no
21182152 // improvement in throughput.
2119- if (rep_->data_block_compressor == nullptr ) {
2153+ if (! rep_->data_block_compressor ) {
21202154 // Force the generally best configuration for no compression: no parallelism
21212155 return ;
21222156 }
@@ -2463,8 +2497,8 @@ void BlockBasedTableBuilder::WritePropertiesBlock(
24632497void BlockBasedTableBuilder::WriteCompressionDictBlock (
24642498 MetaIndexBuilder* meta_index_builder) {
24652499 Slice compression_dict;
2466- if (rep_->compressor_with_dict ) {
2467- compression_dict = rep_->compressor_with_dict ->GetSerializedDict ();
2500+ if (rep_->data_block_compressor ) {
2501+ compression_dict = rep_->data_block_compressor ->GetSerializedDict ();
24682502 }
24692503 if (!compression_dict.empty ()) {
24702504 BlockHandle compression_dict_block_handle;
@@ -2559,6 +2593,7 @@ void BlockBasedTableBuilder::MaybeEnterUnbuffered(
25592593 // The below code is neither safe nor necessary for handling zero data
25602594 // blocks.
25612595 // For PostPopulateCompressionProperties()
2596+ assert (!r->data_block_compressor );
25622597 r->data_block_compressor = r->basic_compressor .get ();
25632598 return ;
25642599 }
@@ -2600,15 +2635,12 @@ void BlockBasedTableBuilder::MaybeEnterUnbuffered(
26002635
26012636 assert (samples.sample_data .size () > 0 );
26022637
2603- // final sample data block flushed, now we can generate dictionary
2604- r->compressor_with_dict = r->basic_compressor ->MaybeCloneSpecialized (
2605- CacheEntryRole::kDataBlock , std::move (samples));
2638+ // final sample data block flushed, now we can generate dictionary (or it
2639+ // might opt not to use a dictionary and that's ok)
2640+ r->data_block_compressor =
2641+ MaybeCloneSpecialized (r->basic_compressor .get (),
2642+ CacheEntryRole::kDataBlock , std::move (samples));
26062643
2607- // The compressor might opt not to use a dictionary, in which case we
2608- // can use the same compressor as for e.g. index blocks.
2609- r->data_block_compressor = r->compressor_with_dict
2610- ? r->compressor_with_dict .get ()
2611- : r->basic_compressor .get ();
26122644 Slice serialized_dict = r->data_block_compressor ->GetSerializedDict ();
26132645 if (r->verify_decompressor ) {
26142646 if (serialized_dict.empty ()) {
@@ -2831,8 +2863,8 @@ uint64_t BlockBasedTableBuilder::EstimatedTailSize() const {
28312863 }
28322864
28332865 // 3. Estimate compression dictionary size
2834- if (rep_->compressor_with_dict ) {
2835- Slice dict = rep_->compressor_with_dict ->GetSerializedDict ();
2866+ if (rep_->data_block_compressor ) {
2867+ Slice dict = rep_->data_block_compressor ->GetSerializedDict ();
28362868 if (!dict.empty ()) {
28372869 estimated_tail_size += dict.size ();
28382870 }
0 commit comments