diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h --- a/llvm/include/llvm/ProfileData/SampleProf.h +++ b/llvm/include/llvm/ProfileData/SampleProf.h @@ -90,7 +90,7 @@ enum SampleProfileFormat { SPF_None = 0, SPF_Text = 0x1, - SPF_Compact_Binary = 0x2, + SPF_Compact_Binary = 0x2, // Deprecated SPF_GCC = 0x3, SPF_Ext_Binary = 0x4, SPF_Binary = 0xff diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h --- a/llvm/include/llvm/ProfileData/SampleProfReader.h +++ b/llvm/include/llvm/ProfileData/SampleProfReader.h @@ -811,41 +811,6 @@ static bool hasFormat(const MemoryBuffer &Buffer); }; -class SampleProfileReaderCompactBinary : public SampleProfileReaderBinary { -private: - /// Function name table. - std::vector NameTable; - /// The table mapping from function name to the offset of its FunctionSample - /// towards file start. - DenseMap FuncOffsetTable; - /// The set containing the functions to use when compiling a module. - DenseSet FuncsToUse; - std::error_code verifySPMagic(uint64_t Magic) override; - std::error_code readNameTable() override; - /// Read a string indirectly via the name table. - ErrorOr readStringFromTable() override; - std::error_code readHeader() override; - std::error_code readFuncOffsetTable(); - -public: - SampleProfileReaderCompactBinary(std::unique_ptr B, - LLVMContext &C) - : SampleProfileReaderBinary(std::move(B), C, SPF_Compact_Binary) {} - - /// \brief Return true if \p Buffer is in the format supported by this class. - static bool hasFormat(const MemoryBuffer &Buffer); - - /// Read samples only for functions to use. - std::error_code readImpl() override; - - /// Collect functions with definitions in Module M. Return true if - /// the reader has been given a module. - bool collectFuncsFromModule() override; - - /// Return whether names in the profile are all MD5 numbers. - bool useMD5() override { return true; } -}; - using InlineCallStack = SmallVector; // Supported histogram types in GCC. Currently, we only need support for diff --git a/llvm/include/llvm/ProfileData/SampleProfWriter.h b/llvm/include/llvm/ProfileData/SampleProfWriter.h --- a/llvm/include/llvm/ProfileData/SampleProfWriter.h +++ b/llvm/include/llvm/ProfileData/SampleProfWriter.h @@ -414,55 +414,6 @@ } }; -// CompactBinary is a compact format of binary profile which both reduces -// the profile size and the load time needed when compiling. It has two -// major difference with Binary format. -// 1. It represents all the strings in name table using md5 hash. -// 2. It saves a function offset table which maps function name index to -// the offset of its function profile to the start of the binary profile, -// so by using the function offset table, for those function profiles which -// will not be needed when compiling a module, the profile reader does't -// have to read them and it saves compile time if the profile size is huge. -// The layout of the compact format is shown as follows: -// -// Part1: Profile header, the same as binary format, containing magic -// number, version, summary, name table... -// Part2: Function Offset Table Offset, which saves the position of -// Part4. -// Part3: Function profile collection -// function1 profile start -// .... -// function2 profile start -// .... -// function3 profile start -// .... -// ...... -// Part4: Function Offset Table -// function1 name index --> function1 profile start -// function2 name index --> function2 profile start -// function3 name index --> function3 profile start -// -// We need Part2 because profile reader can use it to find out and read -// function offset table without reading Part3 first. -class SampleProfileWriterCompactBinary : public SampleProfileWriterBinary { - using SampleProfileWriterBinary::SampleProfileWriterBinary; - -public: - std::error_code writeSample(const FunctionSamples &S) override; - std::error_code write(const SampleProfileMap &ProfileMap) override; - -protected: - /// The table mapping from function name to the offset of its FunctionSample - /// towards profile start. - MapVector FuncOffsetTable; - /// The offset of the slot to be filled with the offset of FuncOffsetTable - /// towards profile start. - uint64_t TableOffset; - std::error_code writeNameTable() override; - std::error_code writeHeader(const SampleProfileMap &ProfileMap) override; - std::error_code writeFuncOffsetTable(); -}; - } // end namespace sampleprof } // end namespace llvm diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp --- a/llvm/lib/ProfileData/SampleProfReader.cpp +++ b/llvm/lib/ProfileData/SampleProfReader.cpp @@ -51,7 +51,7 @@ #define DEBUG_TYPE "samplepgo-reader" // This internal option specifies if the profile uses FS discriminators. -// It only applies to text, binary and compact binary format profiles. +// It only applies to text, and binary format profiles. // For ext-binary format profiles, the flag is set in the summary. static cl::opt ProfileIsFSDisciminator( "profile-isfs", cl::Hidden, cl::init(false), @@ -568,14 +568,6 @@ return SR; } -ErrorOr SampleProfileReaderCompactBinary::readStringFromTable() { - auto Idx = readStringIndex(NameTable); - if (std::error_code EC = Idx.getError()) - return EC; - - return StringRef(NameTable[*Idx]); -} - std::error_code SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) { auto NumSamples = readNumber(); @@ -1012,40 +1004,6 @@ return sampleprof_error::success; } -std::error_code SampleProfileReaderCompactBinary::readImpl() { - // Collect functions used by current module if the Reader has been - // given a module. - bool LoadFuncsToBeUsed = collectFuncsFromModule(); - ProfileIsFS = ProfileIsFSDisciminator; - FunctionSamples::ProfileIsFS = ProfileIsFS; - std::vector OffsetsToUse; - if (!LoadFuncsToBeUsed) { - // load all the function profiles. - for (auto FuncEntry : FuncOffsetTable) { - OffsetsToUse.push_back(FuncEntry.second); - } - } else { - // load function profiles on demand. - for (auto Name : FuncsToUse) { - auto GUID = std::to_string(MD5Hash(Name)); - auto iter = FuncOffsetTable.find(StringRef(GUID)); - if (iter == FuncOffsetTable.end()) - continue; - OffsetsToUse.push_back(iter->second); - } - } - - for (auto Offset : OffsetsToUse) { - const uint8_t *SavedData = Data; - if (std::error_code EC = readFuncProfile( - reinterpret_cast(Buffer->getBufferStart()) + - Offset)) - return EC; - Data = SavedData; - } - return sampleprof_error::success; -} - std::error_code SampleProfileReaderRawBinary::verifySPMagic(uint64_t Magic) { if (Magic == SPMagic()) return sampleprof_error::success; @@ -1058,13 +1016,6 @@ return sampleprof_error::bad_magic; } -std::error_code -SampleProfileReaderCompactBinary::verifySPMagic(uint64_t Magic) { - if (Magic == SPMagic(SPF_Compact_Binary)) - return sampleprof_error::success; - return sampleprof_error::bad_magic; -} - std::error_code SampleProfileReaderBinary::readNameTable() { auto Size = readNumber(); if (std::error_code EC = Size.getError()) @@ -1234,20 +1185,6 @@ return sampleprof_error::success; } -std::error_code SampleProfileReaderCompactBinary::readNameTable() { - auto Size = readNumber(); - if (std::error_code EC = Size.getError()) - return EC; - NameTable.reserve(*Size); - for (uint64_t I = 0; I < *Size; ++I) { - auto FID = readNumber(); - if (std::error_code EC = FID.getError()) - return EC; - NameTable.push_back(std::to_string(*FID)); - } - return sampleprof_error::success; -} - std::error_code SampleProfileReaderExtBinaryBase::readSecHdrTableEntry(uint64_t Idx) { SecHdrTableEntry Entry; @@ -1427,54 +1364,6 @@ return sampleprof_error::success; } -std::error_code SampleProfileReaderCompactBinary::readHeader() { - SampleProfileReaderBinary::readHeader(); - if (std::error_code EC = readFuncOffsetTable()) - return EC; - return sampleprof_error::success; -} - -std::error_code SampleProfileReaderCompactBinary::readFuncOffsetTable() { - auto TableOffset = readUnencodedNumber(); - if (std::error_code EC = TableOffset.getError()) - return EC; - - const uint8_t *SavedData = Data; - const uint8_t *TableStart = - reinterpret_cast(Buffer->getBufferStart()) + - *TableOffset; - Data = TableStart; - - auto Size = readNumber(); - if (std::error_code EC = Size.getError()) - return EC; - - FuncOffsetTable.reserve(*Size); - for (uint64_t I = 0; I < *Size; ++I) { - auto FName(readStringFromTable()); - if (std::error_code EC = FName.getError()) - return EC; - - auto Offset = readNumber(); - if (std::error_code EC = Offset.getError()) - return EC; - - FuncOffsetTable[*FName] = *Offset; - } - End = TableStart; - Data = SavedData; - return sampleprof_error::success; -} - -bool SampleProfileReaderCompactBinary::collectFuncsFromModule() { - if (!M) - return false; - FuncsToUse.clear(); - for (auto &F : *M) - FuncsToUse.insert(FunctionSamples::getCanonicalFnName(F)); - return true; -} - std::error_code SampleProfileReaderBinary::readSummaryEntry( std::vector &Entries) { auto Cutoff = readNumber(); @@ -1545,13 +1434,6 @@ return Magic == SPMagic(SPF_Ext_Binary); } -bool SampleProfileReaderCompactBinary::hasFormat(const MemoryBuffer &Buffer) { - const uint8_t *Data = - reinterpret_cast(Buffer.getBufferStart()); - uint64_t Magic = decodeULEB128(Data); - return Magic == SPMagic(SPF_Compact_Binary); -} - std::error_code SampleProfileReaderGCC::skipNextWord() { uint32_t dummy; if (!GcovBuffer.readInt(dummy)) @@ -1803,7 +1685,7 @@ Ctx.diagnose(DiagnosticInfoSampleProfile( Reader.getBuffer()->getBufferIdentifier(), "Profile data remapping cannot be applied to profile data " - "in compact format (original mangled names are not available).", + "using MD5 names (original mangled names are not available).", DS_Warning)); return; } @@ -1934,8 +1816,6 @@ Reader.reset(new SampleProfileReaderRawBinary(std::move(B), C)); else if (SampleProfileReaderExtBinary::hasFormat(*B)) Reader.reset(new SampleProfileReaderExtBinary(std::move(B), C)); - else if (SampleProfileReaderCompactBinary::hasFormat(*B)) - Reader.reset(new SampleProfileReaderCompactBinary(std::move(B), C)); else if (SampleProfileReaderGCC::hasFormat(*B)) Reader.reset(new SampleProfileReaderGCC(std::move(B), C)); else if (SampleProfileReaderText::hasFormat(*B)) diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp --- a/llvm/lib/ProfileData/SampleProfWriter.cpp +++ b/llvm/lib/ProfileData/SampleProfWriter.cpp @@ -557,15 +557,6 @@ return EC; } -std::error_code -SampleProfileWriterCompactBinary::write(const SampleProfileMap &ProfileMap) { - if (std::error_code EC = SampleProfileWriter::write(ProfileMap)) - return EC; - if (std::error_code EC = writeFuncOffsetTable()) - return EC; - return sampleprof_error::success; -} - /// Write samples to a text file. /// /// Note: it may be tempting to implement this in terms of @@ -712,41 +703,6 @@ return sampleprof_error::success; } -std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() { - auto &OS = *OutputStream; - - // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable. - uint64_t FuncOffsetTableStart = OS.tell(); - support::endian::SeekableWriter Writer(static_cast(OS), - support::little); - Writer.pwrite(FuncOffsetTableStart, TableOffset); - - // Write out the table size. - encodeULEB128(FuncOffsetTable.size(), OS); - - // Write out FuncOffsetTable. - for (auto Entry : FuncOffsetTable) { - if (std::error_code EC = writeNameIdx(Entry.first)) - return EC; - encodeULEB128(Entry.second, OS); - } - FuncOffsetTable.clear(); - return sampleprof_error::success; -} - -std::error_code SampleProfileWriterCompactBinary::writeNameTable() { - auto &OS = *OutputStream; - std::set V; - stablizeNameTable(NameTable, V); - - // Write out the name table. - encodeULEB128(NameTable.size(), OS); - for (auto N : V) { - encodeULEB128(MD5Hash(N), OS); - } - return sampleprof_error::success; -} - std::error_code SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) { auto &OS = *OutputStream; @@ -848,19 +804,6 @@ return sampleprof_error::success; } -std::error_code SampleProfileWriterCompactBinary::writeHeader( - const SampleProfileMap &ProfileMap) { - support::endian::Writer Writer(*OutputStream, support::little); - if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap)) - return EC; - - // Reserve a slot for the offset of function offset table. The slot will - // be populated with the offset of FuncOffsetTable later. - TableOffset = OutputStream->tell(); - Writer.write(static_cast(-2)); - return sampleprof_error::success; -} - std::error_code SampleProfileWriterBinary::writeSummary() { auto &OS = *OutputStream; encodeULEB128(Summary->getTotalCount(), OS); @@ -930,15 +873,6 @@ return writeBody(S); } -std::error_code -SampleProfileWriterCompactBinary::writeSample(const FunctionSamples &S) { - uint64_t Offset = OutputStream->tell(); - StringRef Name = S.getName(); - FuncOffsetTable[Name] = Offset; - encodeULEB128(S.getHeadSamples(), *OutputStream); - return writeBody(S); -} - /// Create a sample profile file writer based on the specified format. /// /// \param Filename The file to create. @@ -950,8 +884,7 @@ SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) { std::error_code EC; std::unique_ptr OS; - if (Format == SPF_Binary || Format == SPF_Ext_Binary || - Format == SPF_Compact_Binary) + if (Format == SPF_Binary || Format == SPF_Ext_Binary) OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None)); else OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_TextWithCRLF)); @@ -976,15 +909,13 @@ // Currently only Text and Extended Binary format are supported for CSSPGO. if ((FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsProbeBased) && - (Format == SPF_Binary || Format == SPF_Compact_Binary)) + Format == SPF_Binary) return sampleprof_error::unsupported_writing_format; if (Format == SPF_Binary) Writer.reset(new SampleProfileWriterRawBinary(OS)); else if (Format == SPF_Ext_Binary) Writer.reset(new SampleProfileWriterExtBinary(OS)); - else if (Format == SPF_Compact_Binary) - Writer.reset(new SampleProfileWriterCompactBinary(OS)); else if (Format == SPF_Text) Writer.reset(new SampleProfileWriterText(OS)); else if (Format == SPF_GCC) diff --git a/llvm/test/Transforms/SampleProfile/Inputs/indirect-call.compact.afdo b/llvm/test/Transforms/SampleProfile/Inputs/indirect-call.compact.afdo deleted file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ extbinary --> md5text -# Trip from text --> compbinary --> md5text -# Compare the two md5 texts -RUN: llvm-profdata merge --sample --compbinary -output=%t.6.profdata %S/Inputs/sample-profile.proftext -RUN: llvm-profdata merge --sample --text -output=%t.6.proftext %t.6.profdata -RUN: llvm-profdata merge --sample --extbinary -use-md5 -output=%t.7.profdata %S/Inputs/sample-profile.proftext -RUN: llvm-profdata merge --sample --text -output=%t.7.proftext %t.7.profdata -RUN: diff -b %t.6.proftext %t.7.proftext diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -53,7 +53,7 @@ enum ProfileFormat { PF_None = 0, PF_Text, - PF_Compact_Binary, + PF_Compact_Binary, // Deprecated PF_Ext_Binary, PF_GCC, PF_Binary @@ -409,8 +409,10 @@ uint64_t MaxTraceLength, bool OutputSparse, unsigned NumThreads, FailureMode FailMode, const StringRef ProfiledBinary) { - if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && - OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) + if (OutputFormat == PF_Compact_Binary) + exitWithError("Compact Binary is deprecated"); + if (OutputFormat != PF_Binary && OutputFormat != PF_Ext_Binary && + OutputFormat != PF_Text) exitWithError("unknown format is specified"); std::unique_ptr Correlator; @@ -912,7 +914,7 @@ static sampleprof::SampleProfileFormat FormatMap[] = { sampleprof::SPF_None, sampleprof::SPF_Text, - sampleprof::SPF_Compact_Binary, + sampleprof::SPF_None, sampleprof::SPF_Ext_Binary, sampleprof::SPF_GCC, sampleprof::SPF_Binary}; @@ -1183,8 +1185,6 @@ cl::desc("Format of output profile"), cl::init(PF_Binary), cl::values( clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), - clEnumValN(PF_Compact_Binary, "compbinary", - "Compact binary encoding"), clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"), clEnumValN(PF_Text, "text", "Text encoding"), clEnumValN(PF_GCC, "gcc", diff --git a/llvm/tools/llvm-profgen/ProfileGenerator.cpp b/llvm/tools/llvm-profgen/ProfileGenerator.cpp --- a/llvm/tools/llvm-profgen/ProfileGenerator.cpp +++ b/llvm/tools/llvm-profgen/ProfileGenerator.cpp @@ -27,7 +27,6 @@ "format", cl::desc("Format of output profile"), cl::init(SPF_Ext_Binary), cl::values( clEnumValN(SPF_Binary, "binary", "Binary encoding (default)"), - clEnumValN(SPF_Compact_Binary, "compbinary", "Compact binary encoding"), clEnumValN(SPF_Ext_Binary, "extbinary", "Extensible binary encoding"), clEnumValN(SPF_Text, "text", "Text encoding"), clEnumValN(SPF_GCC, "gcc", diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp --- a/llvm/unittests/ProfileData/SampleProfTest.cpp +++ b/llvm/unittests/ProfileData/SampleProfTest.cpp @@ -289,11 +289,10 @@ ASSERT_FALSE(CTMap.getError()); // Because _Z3bazi is not defined in module M, expect _Z3bazi's profile - // is not loaded when the profile is ExtBinary or Compact format because - // these formats support loading function profiles on demand. + // is not loaded when the profile is ExtBinary format because this format + // supports loading function profiles on demand. FunctionSamples *ReadBazSamples = Reader->getSamplesFor(BazName); - if (Format == SampleProfileFormat::SPF_Ext_Binary || - Format == SampleProfileFormat::SPF_Compact_Binary) { + if (Format == SampleProfileFormat::SPF_Ext_Binary) { ASSERT_TRUE(ReadBazSamples == nullptr); ASSERT_EQ(3u, Reader->getProfiles().size()); } else { @@ -408,10 +407,6 @@ testRoundTrip(SampleProfileFormat::SPF_Binary, false, false); } -TEST_F(SampleProfTest, roundtrip_compact_binary_profile) { - testRoundTrip(SampleProfileFormat::SPF_Compact_Binary, false, true); -} - TEST_F(SampleProfTest, roundtrip_ext_binary_profile) { testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, false, false); } @@ -473,19 +468,6 @@ testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "", Expected); } -TEST_F(SampleProfTest, default_suffix_elision_compact_binary) { - // Default suffix elision policy: strip everything after first dot. - // This implies that all suffix variants will map to "foo", so - // we don't expect to see any entries for them in the sample - // profile. - StringMap Expected; - Expected["foo"] = uint64_t(20301); - Expected["foo.bar"] = uint64_t(-1); - Expected["foo.llvm.2465"] = uint64_t(-1); - testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "", - Expected); -} - TEST_F(SampleProfTest, selected_suffix_elision_text) { // Profile is created and searched using the "selected" // suffix elision policy: we only strip a .XXX suffix if @@ -498,19 +480,6 @@ testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "selected", Expected); } -TEST_F(SampleProfTest, selected_suffix_elision_compact_binary) { - // Profile is created and searched using the "selected" - // suffix elision policy: we only strip a .XXX suffix if - // it matches a pattern known to be generated by the compiler - // (e.g. ".llvm."). - StringMap Expected; - Expected["foo"] = uint64_t(20301); - Expected["foo.bar"] = uint64_t(20303); - Expected["foo.llvm.2465"] = uint64_t(-1); - testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "selected", - Expected); -} - TEST_F(SampleProfTest, none_suffix_elision_text) { // Profile is created and searched using the "none" // suffix elision policy: no stripping of suffixes at all. @@ -522,16 +491,4 @@ testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "none", Expected); } -TEST_F(SampleProfTest, none_suffix_elision_compact_binary) { - // Profile is created and searched using the "none" - // suffix elision policy: no stripping of suffixes at all. - // Here we expect to see all variants in the profile. - StringMap Expected; - Expected["foo"] = uint64_t(20301); - Expected["foo.bar"] = uint64_t(20303); - Expected["foo.llvm.2465"] = uint64_t(20305); - testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "none", - Expected); -} - } // end anonymous namespace diff --git a/llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp b/llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp --- a/llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp +++ b/llvm/unittests/tools/llvm-profdata/OutputSizeLimitTest.cpp @@ -123,24 +123,6 @@ RETURN_IF_ERROR(sys::fs::file_size(Temp.path(), FileSize)); EXPECT_LE(FileSize, SizeLimit); - // For compact binary format, function names are stored as MD5, so we cannot - // directly match the samples of the new profile with the old profile. A - // simple way is to convert the old profile to compact binary format and read - // it back - if (Format == llvm::sampleprof::SPF_Compact_Binary) { - TempFile CompBinary("compbinary", "afdo", "", true); - { - DEF_VAR_RETURN_IF_ERROR( - Writer, SampleProfileWriter::create( - CompBinary.path(), llvm::sampleprof::SPF_Compact_Binary)); - RETURN_IF_ERROR(Writer->write(OldProfiles)); - } - VAR_RETURN_IF_ERROR(Reader, SampleProfileReader::create( - CompBinary.path().str(), Context, *FS)); - RETURN_IF_ERROR(Reader->read()); - OldProfiles = Reader->getProfiles(); - } - // For every sample in the new profile, confirm it is in the old profile and // unchanged. for (auto Sample : NewProfiles) { @@ -169,13 +151,6 @@ Succeeded()); } -TEST(TestOutputSizeLimit, TestOutputSizeLimitCompBinary) { - for (size_t OutputSizeLimit : {277, 276, 275, 264, 263, 250, 200}) - ASSERT_THAT_EXPECTED( - RunTest(Input1, OutputSizeLimit, llvm::sampleprof::SPF_Compact_Binary), - Succeeded()); -} - TEST(TestOutputSizeLimit, TestOutputSizeLimitText) { for (size_t OutputSizeLimit : {229, 228, 227, 213, 212, 211, 189, 188, 187, 186, 150})