Skip to content

Commit 5102588

Browse files
authored
Remove special case handling for missing target field (#1036)
1 parent 0108a6e commit 5102588

12 files changed

+38
-127
lines changed

include/api/CDataFrameAnalysisRunner.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,6 @@ class API_EXPORT CDataFrameAnalysisRunner {
103103
//! \return The number of columns this analysis appends.
104104
virtual std::size_t numberExtraColumns() const = 0;
105105

106-
//! \return Indicator of columns for which empty value should be treated as missing.
107-
virtual TBoolVec columnsForWhichEmptyIsMissing(const TStrVec& fieldNames) const;
108-
109106
//! Write the extra columns of \p row added by the analysis to \p writer.
110107
//!
111108
//! This should create a new object of the form:

include/api/CDataFrameAnalysisSpecification.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,22 +172,22 @@ class API_EXPORT CDataFrameAnalysisSpecification {
172172
//! \note The commit of the results of the analysis is atomic per partition.
173173
//! \warning This assumes that there is no access to the data frame in the
174174
//! calling thread until the runner has finished.
175-
CDataFrameAnalysisRunner* run(core::CDataFrame& frame) const;
175+
CDataFrameAnalysisRunner*
176+
run(core::CDataFrame& frame,
177+
core::CRapidJsonConcurrentLineWriter* outputWriter = nullptr) const;
176178

177179
//! Estimates memory usage in two cases:
178180
//! 1. disk is not used (the whole data frame fits in main memory)
179181
//! 2. disk is used (only one partition needs to be loaded to main memory)
180182
void estimateMemoryUsage(CMemoryUsageEstimationResultJsonWriter& writer) const;
181183

182-
//! \return Indicator of columns for which empty value should be treated as missing.
183-
TBoolVec columnsForWhichEmptyIsMissing(const TStrVec& fieldNames) const;
184-
185-
//! \return shared pointer to the persistence stream.
184+
//! \return The stream to which to persist state if there is one.
186185
TDataAdderUPtr persister() const;
187186

187+
//! \return The stream from which to retore state if there is one.
188188
TDataSearcherUPtr restoreSearcher() const;
189189

190-
//! Get pointer to the analysis runner.
190+
//! \return The analysis runner.
191191
CDataFrameAnalysisRunner* runner();
192192

193193
private:

include/api/CDataFrameTrainBoostedTreeClassifierRunner.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ class API_EXPORT CDataFrameTrainBoostedTreeClassifierRunner final
4040
CDataFrameTrainBoostedTreeClassifierRunner(const CDataFrameAnalysisSpecification& spec,
4141
const CDataFrameAnalysisParameters& parameters);
4242

43-
//! \return Indicator of columns for which empty value should be treated as missing.
44-
TBoolVec columnsForWhichEmptyIsMissing(const TStrVec& fieldNames) const override;
45-
4643
//! Write the prediction for \p row to \p writer.
4744
void writeOneRow(const core::CDataFrame& frame,
4845
const TRowRef& row,

include/core/CDataFrame.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,6 @@ class CORE_EXPORT CDataFrame final {
449449
//! Write the string which indicates that a value is missing.
450450
void missingString(std::string missing);
451451

452-
//! Write for which columns an empty string implies the value is missing.
453-
void emptyIsMissing(TBoolVec emptyIsMissing);
454-
455452
//! Write which columns contain categorical data.
456453
void categoricalColumns(TStrVec categoricalColumnNames);
457454

@@ -586,11 +583,6 @@ class CORE_EXPORT CDataFrame final {
586583
//! The string which indicates that a category is missing.
587584
std::string m_MissingString;
588585

589-
//! Indicator vector for treating empty strings as missing values.
590-
// TODO Remove once Java passes the correct value for the missing target
591-
// for classification.
592-
TBoolVec m_EmptyIsMissing;
593-
594586
//! Indicator vector of the columns which contain categorical values.
595587
TBoolVec m_ColumnIsCategorical;
596588

lib/api/CDataFrameAnalysisRunner.cc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,6 @@ CDataFrameAnalysisRunner::~CDataFrameAnalysisRunner() {
4242
this->waitToFinish();
4343
}
4444

45-
TBoolVec CDataFrameAnalysisRunner::columnsForWhichEmptyIsMissing(const TStrVec& fieldNames) const {
46-
return TBoolVec(fieldNames.size(), false);
47-
}
48-
4945
void CDataFrameAnalysisRunner::estimateMemoryUsage(CMemoryUsageEstimationResultJsonWriter& writer) const {
5046
std::size_t numberRows{m_Spec.numberRows()};
5147
std::size_t numberColumns{m_Spec.numberColumns()};

lib/api/CDataFrameAnalysisSpecification.cc

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,14 @@ CDataFrameAnalysisSpecification::CDataFrameAnalysisSpecification(
118118
rapidjson::Document specification;
119119
if (specification.Parse(jsonSpecification.c_str()) == false) {
120120
HANDLE_FATAL(<< "Input error: failed to parse analysis specification '"
121-
<< jsonSpecification << "'. Please report this problem.");
121+
<< jsonSpecification << "'. Please report this problem.")
122122
} else {
123123

124124
auto parameters = CONFIG_READER.read(specification);
125125

126126
for (auto name : {ROWS, COLS, MEMORY_LIMIT, THREADS}) {
127127
if (parameters[name].as<std::size_t>() == 0) {
128-
HANDLE_FATAL(<< "Input error: '" << name << "' must be non-zero");
128+
HANDLE_FATAL(<< "Input error: '" << name << "' must be non-zero")
129129
}
130130
}
131131
m_NumberRows = parameters[ROWS].as<std::size_t>();
@@ -207,8 +207,11 @@ CDataFrameAnalysisSpecification::makeDataFrame() {
207207
return result;
208208
}
209209

210-
CDataFrameAnalysisRunner* CDataFrameAnalysisSpecification::run(core::CDataFrame& frame) const {
210+
CDataFrameAnalysisRunner*
211+
CDataFrameAnalysisSpecification::run(core::CDataFrame& frame,
212+
core::CRapidJsonConcurrentLineWriter* writer) const {
211213
if (m_Runner != nullptr) {
214+
m_Runner->instrumentation().writer(writer);
212215
m_Runner->run(frame);
213216
return m_Runner.get();
214217
}
@@ -218,20 +221,12 @@ CDataFrameAnalysisRunner* CDataFrameAnalysisSpecification::run(core::CDataFrame&
218221
void CDataFrameAnalysisSpecification::estimateMemoryUsage(CMemoryUsageEstimationResultJsonWriter& writer) const {
219222
if (m_Runner == nullptr) {
220223
HANDLE_FATAL(<< "Internal error: no runner available so can't estimate memory."
221-
<< " Please report this problem.");
224+
<< " Please report this problem.")
222225
return;
223226
}
224227
m_Runner->estimateMemoryUsage(writer);
225228
}
226229

227-
TBoolVec CDataFrameAnalysisSpecification::columnsForWhichEmptyIsMissing(const TStrVec& fieldNames) const {
228-
if (m_Runner == nullptr) {
229-
HANDLE_FATAL(<< "Internal error: no runner available. Please report this problem.");
230-
return TBoolVec(fieldNames.size(), false);
231-
}
232-
return m_Runner->columnsForWhichEmptyIsMissing(fieldNames);
233-
}
234-
235230
void CDataFrameAnalysisSpecification::initializeRunner(const rapidjson::Value& jsonAnalysis) {
236231
// We pass of the interpretation of the parameters object to the appropriate
237232
// analysis runner.
@@ -251,7 +246,7 @@ void CDataFrameAnalysisSpecification::initializeRunner(const rapidjson::Value& j
251246
}
252247

253248
HANDLE_FATAL(<< "Input error: unexpected analysis name '" << m_AnalysisName
254-
<< "'. Please report this problem.");
249+
<< "'. Please report this problem.")
255250
}
256251

257252
CDataFrameAnalysisSpecification::TDataAdderUPtr

lib/api/CDataFrameAnalyzer.cc

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,13 @@ void CDataFrameAnalyzer::run() {
132132
auto outStream = m_ResultsStreamSupplier();
133133
core::CRapidJsonConcurrentLineWriter outputWriter{*outStream};
134134

135-
CDataFrameAnalysisRunner* analysis{m_AnalysisSpecification->runner()};
136-
if (analysis == nullptr) {
137-
return;
135+
auto analysisRunner = m_AnalysisSpecification->run(*m_DataFrame, &outputWriter);
136+
137+
if (analysisRunner != nullptr) {
138+
this->monitorProgress(*analysisRunner, outputWriter);
139+
analysisRunner->waitToFinish();
140+
this->writeResultsOf(*analysisRunner, outputWriter);
138141
}
139-
analysis->instrumentation().writer(&outputWriter);
140-
m_AnalysisSpecification->run(*m_DataFrame);
141-
this->monitorProgress(*analysis, outputWriter);
142-
analysis->waitToFinish();
143-
this->writeResultsOf(*analysis, outputWriter);
144142
}
145143

146144
const CDataFrameAnalyzer::TTemporaryDirectoryPtr& CDataFrameAnalyzer::dataFrameDirectory() const {
@@ -248,8 +246,6 @@ void CDataFrameAnalyzer::captureFieldNames(const TStrVec& fieldNames) {
248246
TStrVec columnNames{fieldNames.begin() + m_BeginDataFieldValues,
249247
fieldNames.begin() + m_EndDataFieldValues};
250248
m_DataFrame->columnNames(columnNames);
251-
m_DataFrame->emptyIsMissing(
252-
m_AnalysisSpecification->columnsForWhichEmptyIsMissing(columnNames));
253249
m_DataFrame->categoricalColumns(m_AnalysisSpecification->categoricalFieldNames());
254250
m_CapturedFieldNames = true;
255251
}

lib/api/CDataFrameTrainBoostedTreeClassifierRunner.cc

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,6 @@ CDataFrameTrainBoostedTreeClassifierRunner::CDataFrameTrainBoostedTreeClassifier
9696
}
9797
}
9898

99-
TBoolVec CDataFrameTrainBoostedTreeClassifierRunner::columnsForWhichEmptyIsMissing(
100-
const TStrVec& fieldNames) const {
101-
// The only field for which empty value should be treated as missing is dependent
102-
// variable which has empty value for non-training rows.
103-
TBoolVec emptyAsMissing(fieldNames.size(), false);
104-
auto pos = std::find(fieldNames.begin(), fieldNames.end(),
105-
this->dependentVariableFieldName());
106-
if (pos != fieldNames.end()) {
107-
emptyAsMissing[pos - fieldNames.begin()] = true;
108-
}
109-
return emptyAsMissing;
110-
}
111-
11299
void CDataFrameTrainBoostedTreeClassifierRunner::writeOneRow(
113100
const core::CDataFrame& frame,
114101
const TRowRef& row,

lib/api/unittest/CDataFrameAnalysisRunnerTest.cc

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -204,31 +204,4 @@ BOOST_AUTO_TEST_CASE(testEstimateMemoryUsageFor1000Rows) {
204204
testEstimateMemoryUsage(1000, "403kB", "142kB", 0);
205205
}
206206

207-
void testColumnsForWhichEmptyIsMissing(const std::string& analysis,
208-
const std::string& dependentVariableName,
209-
const TStrVec& fieldNames,
210-
const TStrVec& categoricalFields,
211-
const TBoolVec& expectedEmptyIsMissing) {
212-
std::string parameters{"{\"dependent_variable\": \"" + dependentVariableName + "\"}"};
213-
std::string jsonSpec{api::CDataFrameAnalysisSpecificationJsonWriter::jsonString(
214-
"testJob", 10000, 5, 100000000, 1, "", categoricalFields, true,
215-
test::CTestTmpDir::tmpDir(), "", analysis, parameters)};
216-
api::CDataFrameAnalysisSpecification spec{jsonSpec};
217-
auto emptyIsMissing = spec.columnsForWhichEmptyIsMissing(fieldNames);
218-
BOOST_REQUIRE_EQUAL(core::CContainerPrinter::print(expectedEmptyIsMissing),
219-
core::CContainerPrinter::print(emptyIsMissing));
220-
}
221-
222-
BOOST_AUTO_TEST_CASE(testColumnsForWhichEmptyIsMissingClassification) {
223-
testColumnsForWhichEmptyIsMissing("classification", "class",
224-
{"feature_1", "feature_2", "feature_3", "class"},
225-
{"class"}, {false, false, false, true});
226-
}
227-
228-
BOOST_AUTO_TEST_CASE(testColumnsForWhichEmptyIsMissingRegression) {
229-
testColumnsForWhichEmptyIsMissing("regression", "value",
230-
{"feature_1", "feature_2", "feature_3", "value"},
231-
{}, {false, false, false, false});
232-
}
233-
234207
BOOST_AUTO_TEST_SUITE_END()

lib/api/unittest/CDataFrameAnalysisSpecificationTest.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ BOOST_AUTO_TEST_CASE(testRunAnalysis) {
405405
BOOST_TEST_REQUIRE(runner->instrumentation().progress() <= 1.0);
406406
}
407407

408-
LOG_DEBUG(<< "final progress = " << lastProgress);
408+
LOG_TRACE(<< "final progress = " << lastProgress);
409409
BOOST_REQUIRE_EQUAL(1.0, runner->instrumentation().progress());
410410
}
411411
}

lib/api/unittest/CDataFrameAnalyzerTrainingTest.cc

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ BOOST_AUTO_TEST_CASE(testRunBoostedTreeRegressionTrainingWithRowsMissingTargetVa
653653
}
654654
for (std::size_t i = 40; i < 50; ++i) {
655655
fieldValues[0] = std::to_string(feature[i]);
656-
fieldValues[1] = "";
656+
fieldValues[1] = core::CDataFrame::DEFAULT_MISSING_STRING;
657657
fieldValues[2] = std::to_string(i);
658658
analyzer.handleRecord(fieldNames, fieldValues);
659659
}
@@ -985,10 +985,8 @@ BOOST_AUTO_TEST_CASE(testCategoricalFieldsEmptyAsMissing) {
985985
return [expected](double actual) { return expected == actual; };
986986
};
987987

988-
auto missing = []() {
989-
return [](double actual) {
990-
return maths::CDataFrameUtils::isMissing(actual);
991-
};
988+
auto missing = [](double actual) {
989+
return maths::CDataFrameUtils::isMissing(actual);
992990
};
993991

994992
auto assertRow = [&](const std::size_t row_i,
@@ -1007,23 +1005,26 @@ BOOST_AUTO_TEST_CASE(testCategoricalFieldsEmptyAsMissing) {
10071005
return std::make_unique<core::CJsonOutputStreamWrapper>(output);
10081006
};
10091007

1008+
std::string missingString{"foo"};
1009+
10101010
test::CDataFrameAnalysisSpecificationFactory specFactory;
10111011
api::CDataFrameAnalyzer analyzer{
10121012
specFactory.rows(1000)
10131013
.memoryLimit(27000000)
10141014
.predictionCategoricalFieldNames({"x1", "x2", "x5"})
1015+
.missingString(missingString)
10151016
.predictionSpec(test::CDataFrameAnalysisSpecificationFactory::classification(), "x5"),
10161017
outputWriterFactory};
10171018

10181019
TStrVec fieldNames{"x1", "x2", "x3", "x4", "x5", ".", "."};
10191020
analyzer.handleRecord(fieldNames, {"x11", "x21", "0", "0", "x51", "0", ""});
10201021
analyzer.handleRecord(fieldNames, {"x12", "x22", "1", "1", "x52", "1", ""});
10211022
analyzer.handleRecord(fieldNames, {"", "x23", "2", "2", "x51", "2", ""});
1022-
analyzer.handleRecord(fieldNames, {"x14", "x24", "3", "3", "", "3", ""});
1023+
analyzer.handleRecord(fieldNames, {"x14", "x24", "3", "3", missingString, "3", ""});
10231024
analyzer.handleRecord(fieldNames, {"x15", "x25", "4", "4", "x51", "4", ""});
10241025
analyzer.handleRecord(fieldNames, {"x11", "x26", "5", "5", "x52", "5", ""});
1025-
analyzer.handleRecord(fieldNames, {"x12", "", "6", "6", "", "6", ""});
1026-
analyzer.handleRecord(fieldNames, {"x13", "x21", "7", "7", "", "7", ""});
1026+
analyzer.handleRecord(fieldNames, {"x12", "", "6", "6", missingString, "6", ""});
1027+
analyzer.handleRecord(fieldNames, {"x13", "x21", "7", "7", missingString, "7", ""});
10271028
analyzer.handleRecord(fieldNames, {"x14", "x22", "8", "8", "x51", "8", ""});
10281029
analyzer.handleRecord(fieldNames, {"", "x23", "9", "9", "x52", "9", ""});
10291030
analyzer.receivedAllRows();
@@ -1036,11 +1037,11 @@ BOOST_AUTO_TEST_CASE(testCategoricalFieldsEmptyAsMissing) {
10361037
assertRow(0, {eq(0.0), eq(0.0), eq(0.0), eq(0.0), eq(0.0)}, rows[0]);
10371038
assertRow(1, {eq(1.0), eq(1.0), eq(1.0), eq(1.0), eq(1.0)}, rows[1]);
10381039
assertRow(2, {eq(2.0), eq(2.0), eq(2.0), eq(2.0), eq(0.0)}, rows[2]);
1039-
assertRow(3, {eq(3.0), eq(3.0), eq(3.0), eq(3.0), missing()}, rows[3]);
1040+
assertRow(3, {eq(3.0), eq(3.0), eq(3.0), eq(3.0), missing}, rows[3]);
10401041
assertRow(4, {eq(4.0), eq(4.0), eq(4.0), eq(4.0), eq(0.0)}, rows[4]);
10411042
assertRow(5, {eq(0.0), eq(5.0), eq(5.0), eq(5.0), eq(1.0)}, rows[5]);
1042-
assertRow(6, {eq(1.0), eq(6.0), eq(6.0), eq(6.0), missing()}, rows[6]);
1043-
assertRow(7, {eq(5.0), eq(0.0), eq(7.0), eq(7.0), missing()}, rows[7]);
1043+
assertRow(6, {eq(1.0), eq(6.0), eq(6.0), eq(6.0), missing}, rows[6]);
1044+
assertRow(7, {eq(5.0), eq(0.0), eq(7.0), eq(7.0), missing}, rows[7]);
10441045
assertRow(8, {eq(3.0), eq(1.0), eq(8.0), eq(8.0), eq(0.0)}, rows[8]);
10451046
assertRow(9, {eq(2.0), eq(2.0), eq(9.0), eq(9.0), eq(1.0)}, rows[9]);
10461047
});

lib/core/CDataFrame.cc

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ CDataFrame::CDataFrame(bool inMainMemory,
127127
m_ReadAndWriteToStoreSyncStrategy{readAndWriteToStoreSyncStrategy},
128128
m_WriteSliceToStore{writeSliceToStore}, m_ColumnNames(numberColumns),
129129
m_CategoricalColumnValues(numberColumns), m_MissingString{DEFAULT_MISSING_STRING},
130-
m_EmptyIsMissing(numberColumns, false),
131130
m_ColumnIsCategorical(numberColumns, false) {
132131
}
133132

@@ -169,7 +168,6 @@ void CDataFrame::resizeColumns(std::size_t numberThreads, std::size_t numberColu
169168
this->reserve(numberThreads, numberColumns);
170169
m_ColumnNames.resize(numberColumns);
171170
m_CategoricalColumnValues.resize(numberColumns);
172-
m_EmptyIsMissing.resize(numberColumns, false);
173171
m_ColumnIsCategorical.resize(numberColumns, false);
174172
m_NumberColumns = numberColumns;
175173
}
@@ -215,19 +213,13 @@ CDataFrame::TRowFuncVecBoolPr CDataFrame::writeColumns(std::size_t numberThreads
215213
void CDataFrame::parseAndWriteRow(const TStrCRng& columnValues, const std::string* hash) {
216214

217215
auto stringToValue = [this](bool isCategorical, TStrSizeUMap& categoryLookup,
218-
TStrVec& categories, bool emptyIsMissing,
219-
const std::string& columnValue) {
216+
TStrVec& categories, const std::string& columnValue) {
220217
if (columnValue == m_MissingString) {
221218
++m_MissingValueCount;
222219
return core::CFloatStorage{valueOfMissing()};
223220
}
224221

225222
if (isCategorical) {
226-
// TODO Remove when Java passes special missing value string.
227-
if (columnValue.empty() && emptyIsMissing) {
228-
return core::CFloatStorage{valueOfMissing()};
229-
}
230-
231223
// This encodes in a format suitable for efficient storage. The
232224
// actual encoding approach is chosen when the analysis runs.
233225
std::size_t id;
@@ -257,11 +249,7 @@ void CDataFrame::parseAndWriteRow(const TStrCRng& columnValues, const std::strin
257249
// otherwise we must impute or exit with failure.
258250

259251
double value;
260-
if (columnValue.empty()) {
261-
// TODO Remove when Java passes special missing value string.
262-
++m_MissingValueCount;
263-
return core::CFloatStorage{valueOfMissing()};
264-
} else if (core::CStringUtils::stringToTypeSilent(columnValue, value) == false) {
252+
if (core::CStringUtils::stringToTypeSilent(columnValue, value) == false) {
265253
++m_BadValueCount;
266254
return core::CFloatStorage{valueOfMissing()};
267255
}
@@ -278,9 +266,9 @@ void CDataFrame::parseAndWriteRow(const TStrCRng& columnValues, const std::strin
278266

279267
this->writeRow([&](TFloatVecItr columns, std::int32_t& docHash) {
280268
for (std::size_t i = 0; i < columnValues.size(); ++i, ++columns) {
281-
*columns = stringToValue(
282-
m_ColumnIsCategorical[i], m_CategoricalColumnValueLookup[i],
283-
m_CategoricalColumnValues[i], m_EmptyIsMissing[i], columnValues[i]);
269+
*columns = stringToValue(m_ColumnIsCategorical[i],
270+
m_CategoricalColumnValueLookup[i],
271+
m_CategoricalColumnValues[i], columnValues[i]);
284272
}
285273
docHash = 0;
286274
if (hash != nullptr &&
@@ -312,16 +300,6 @@ void CDataFrame::missingString(std::string missing) {
312300
m_MissingString = std::move(missing);
313301
}
314302

315-
void CDataFrame::emptyIsMissing(TBoolVec emptyIsMissing) {
316-
if (emptyIsMissing.size() != m_NumberColumns) {
317-
HANDLE_FATAL(<< "Internal error: expected '" << m_NumberColumns
318-
<< "' 'empty is missing' column indicator values but got "
319-
<< CContainerPrinter::print(emptyIsMissing));
320-
} else {
321-
m_EmptyIsMissing = std::move(emptyIsMissing);
322-
}
323-
}
324-
325303
void CDataFrame::categoricalColumns(TStrVec categoricalColumnNames) {
326304
std::sort(categoricalColumnNames.begin(), categoricalColumnNames.end());
327305
for (std::size_t i = 0; i < m_ColumnNames.size(); ++i) {
@@ -387,7 +365,6 @@ std::size_t CDataFrame::memoryUsage() const {
387365
memory += CMemory::dynamicSize(m_CategoricalColumnValues);
388366
memory += CMemory::dynamicSize(m_CategoricalColumnValueLookup);
389367
memory += CMemory::dynamicSize(m_MissingString);
390-
memory += CMemory::dynamicSize(m_EmptyIsMissing);
391368
memory += CMemory::dynamicSize(m_ColumnIsCategorical);
392369
memory += CMemory::dynamicSize(m_Slices);
393370
memory += CMemory::dynamicSize(m_Writer);

0 commit comments

Comments
 (0)