@@ -906,6 +906,17 @@ CreateFileHandler(MemoryBuffer &FirstInput,
906
906
}
907
907
908
908
OffloadBundlerConfig::OffloadBundlerConfig () {
909
+ if (llvm::compression::zstd::isAvailable ()) {
910
+ CompressionFormat = llvm::compression::Format::Zstd;
911
+ // Compression level 3 is usually sufficient for zstd since long distance
912
+ // matching is enabled.
913
+ CompressionLevel = 3 ;
914
+ } else if (llvm::compression::zlib::isAvailable ()) {
915
+ CompressionFormat = llvm::compression::Format::Zlib;
916
+ // Use default level for zlib since higher level does not have significant
917
+ // improvement.
918
+ CompressionLevel = llvm::compression::zlib::DefaultCompression;
919
+ }
909
920
auto IgnoreEnvVarOpt =
910
921
llvm::sys::Process::GetEnv (" OFFLOAD_BUNDLER_IGNORE_ENV_VAR" );
911
922
if (IgnoreEnvVarOpt.has_value () && IgnoreEnvVarOpt.value () == " 1" )
@@ -919,11 +930,41 @@ OffloadBundlerConfig::OffloadBundlerConfig() {
919
930
llvm::sys::Process::GetEnv (" OFFLOAD_BUNDLER_COMPRESS" );
920
931
if (CompressEnvVarOpt.has_value ())
921
932
Compress = CompressEnvVarOpt.value () == " 1" ;
933
+
934
+ auto CompressionLevelEnvVarOpt =
935
+ llvm::sys::Process::GetEnv (" OFFLOAD_BUNDLER_COMPRESSION_LEVEL" );
936
+ if (CompressionLevelEnvVarOpt.has_value ()) {
937
+ llvm::StringRef CompressionLevelStr = CompressionLevelEnvVarOpt.value ();
938
+ int Level;
939
+ if (!CompressionLevelStr.getAsInteger (10 , Level))
940
+ CompressionLevel = Level;
941
+ else
942
+ llvm::errs ()
943
+ << " Warning: Invalid value for OFFLOAD_BUNDLER_COMPRESSION_LEVEL: "
944
+ << CompressionLevelStr.str () << " . Ignoring it.\n " ;
945
+ }
946
+ }
947
+
948
+ // Utility function to format numbers with commas
949
+ static std::string formatWithCommas (unsigned long long Value) {
950
+ std::string Num = std::to_string (Value);
951
+ int InsertPosition = Num.length () - 3 ;
952
+ while (InsertPosition > 0 ) {
953
+ Num.insert (InsertPosition, " ," );
954
+ InsertPosition -= 3 ;
955
+ }
956
+ return Num;
922
957
}
923
958
924
959
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
925
- CompressedOffloadBundle::compress (const llvm::MemoryBuffer &Input,
960
+ CompressedOffloadBundle::compress (llvm::compression::Params P,
961
+ const llvm::MemoryBuffer &Input,
926
962
bool Verbose) {
963
+ if (!llvm::compression::zstd::isAvailable () &&
964
+ !llvm::compression::zlib::isAvailable ())
965
+ return createStringError (llvm::inconvertibleErrorCode (),
966
+ " Compression not supported" );
967
+
927
968
llvm::Timer HashTimer (" Hash Calculation Timer" , " Hash calculation time" ,
928
969
ClangOffloadBundlerTimerGroup);
929
970
if (Verbose)
@@ -941,33 +982,29 @@ CompressedOffloadBundle::compress(const llvm::MemoryBuffer &Input,
941
982
reinterpret_cast <const uint8_t *>(Input.getBuffer ().data ()),
942
983
Input.getBuffer ().size ());
943
984
944
- llvm::compression::Format CompressionFormat;
945
-
946
- if (llvm::compression::zstd::isAvailable ())
947
- CompressionFormat = llvm::compression::Format::Zstd;
948
- else if (llvm::compression::zlib::isAvailable ())
949
- CompressionFormat = llvm::compression::Format::Zlib;
950
- else
951
- return createStringError (llvm::inconvertibleErrorCode (),
952
- " Compression not supported" );
953
-
954
985
llvm::Timer CompressTimer (" Compression Timer" , " Compression time" ,
955
986
ClangOffloadBundlerTimerGroup);
956
987
if (Verbose)
957
988
CompressTimer.startTimer ();
958
- llvm::compression::compress (CompressionFormat , BufferUint8, CompressedBuffer);
989
+ llvm::compression::compress (P , BufferUint8, CompressedBuffer);
959
990
if (Verbose)
960
991
CompressTimer.stopTimer ();
961
992
962
- uint16_t CompressionMethod = static_cast <uint16_t >(CompressionFormat );
993
+ uint16_t CompressionMethod = static_cast <uint16_t >(P. format );
963
994
uint32_t UncompressedSize = Input.getBuffer ().size ();
995
+ uint32_t TotalFileSize = MagicNumber.size () + sizeof (TotalFileSize) +
996
+ sizeof (Version) + sizeof (CompressionMethod) +
997
+ sizeof (UncompressedSize) + sizeof (TruncatedHash) +
998
+ CompressedBuffer.size ();
964
999
965
1000
SmallVector<char , 0 > FinalBuffer;
966
1001
llvm::raw_svector_ostream OS (FinalBuffer);
967
1002
OS << MagicNumber;
968
1003
OS.write (reinterpret_cast <const char *>(&Version), sizeof (Version));
969
1004
OS.write (reinterpret_cast <const char *>(&CompressionMethod),
970
1005
sizeof (CompressionMethod));
1006
+ OS.write (reinterpret_cast <const char *>(&TotalFileSize),
1007
+ sizeof (TotalFileSize));
971
1008
OS.write (reinterpret_cast <const char *>(&UncompressedSize),
972
1009
sizeof (UncompressedSize));
973
1010
OS.write (reinterpret_cast <const char *>(&TruncatedHash),
@@ -977,17 +1014,31 @@ CompressedOffloadBundle::compress(const llvm::MemoryBuffer &Input,
977
1014
978
1015
if (Verbose) {
979
1016
auto MethodUsed =
980
- CompressionFormat == llvm::compression::Format::Zstd ? " zstd" : " zlib" ;
1017
+ P.format == llvm::compression::Format::Zstd ? " zstd" : " zlib" ;
1018
+ double CompressionRate =
1019
+ static_cast <double >(UncompressedSize) / CompressedBuffer.size ();
1020
+ double CompressionTimeSeconds = CompressTimer.getTotalTime ().getWallTime ();
1021
+ double CompressionSpeedMBs =
1022
+ (UncompressedSize / (1024.0 * 1024.0 )) / CompressionTimeSeconds;
1023
+
981
1024
llvm::errs () << " Compressed bundle format version: " << Version << " \n "
1025
+ << " Total file size (including headers): "
1026
+ << formatWithCommas (TotalFileSize) << " bytes\n "
982
1027
<< " Compression method used: " << MethodUsed << " \n "
983
- << " Binary size before compression: " << UncompressedSize
984
- << " bytes\n "
985
- << " Binary size after compression: " << CompressedBuffer.size ()
986
- << " bytes\n "
1028
+ << " Compression level: " << P.level << " \n "
1029
+ << " Binary size before compression: "
1030
+ << formatWithCommas (UncompressedSize) << " bytes\n "
1031
+ << " Binary size after compression: "
1032
+ << formatWithCommas (CompressedBuffer.size ()) << " bytes\n "
1033
+ << " Compression rate: "
1034
+ << llvm::format (" %.2lf" , CompressionRate) << " \n "
1035
+ << " Compression ratio: "
1036
+ << llvm::format (" %.2lf%%" , 100.0 / CompressionRate) << " \n "
1037
+ << " Compression speed: "
1038
+ << llvm::format (" %.2lf MB/s" , CompressionSpeedMBs) << " \n "
987
1039
<< " Truncated MD5 hash: "
988
1040
<< llvm::format_hex (TruncatedHash, 16 ) << " \n " ;
989
1041
}
990
-
991
1042
return llvm::MemoryBuffer::getMemBufferCopy (
992
1043
llvm::StringRef (FinalBuffer.data (), FinalBuffer.size ()));
993
1044
}
@@ -998,31 +1049,42 @@ CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
998
1049
999
1050
StringRef Blob = Input.getBuffer ();
1000
1051
1001
- if (Blob.size () < HeaderSize) {
1052
+ if (Blob.size () < V1HeaderSize)
1002
1053
return llvm::MemoryBuffer::getMemBufferCopy (Blob);
1003
- }
1054
+
1004
1055
if (llvm::identify_magic (Blob) !=
1005
1056
llvm::file_magic::offload_bundle_compressed) {
1006
1057
if (Verbose)
1007
1058
llvm::errs () << " Uncompressed bundle.\n " ;
1008
1059
return llvm::MemoryBuffer::getMemBufferCopy (Blob);
1009
1060
}
1010
1061
1062
+ size_t CurrentOffset = MagicSize;
1063
+
1011
1064
uint16_t ThisVersion;
1065
+ memcpy (&ThisVersion, Blob.data () + CurrentOffset, sizeof (uint16_t ));
1066
+ CurrentOffset += VersionFieldSize;
1067
+
1012
1068
uint16_t CompressionMethod;
1069
+ memcpy (&CompressionMethod, Blob.data () + CurrentOffset, sizeof (uint16_t ));
1070
+ CurrentOffset += MethodFieldSize;
1071
+
1072
+ uint32_t TotalFileSize;
1073
+ if (ThisVersion >= 2 ) {
1074
+ if (Blob.size () < V2HeaderSize)
1075
+ return createStringError (inconvertibleErrorCode (),
1076
+ " Compressed bundle header size too small" );
1077
+ memcpy (&TotalFileSize, Blob.data () + CurrentOffset, sizeof (uint32_t ));
1078
+ CurrentOffset += FileSizeFieldSize;
1079
+ }
1080
+
1013
1081
uint32_t UncompressedSize;
1082
+ memcpy (&UncompressedSize, Blob.data () + CurrentOffset, sizeof (uint32_t ));
1083
+ CurrentOffset += UncompressedSizeFieldSize;
1084
+
1014
1085
uint64_t StoredHash;
1015
- memcpy (&ThisVersion, Input.getBuffer ().data () + MagicNumber.size (),
1016
- sizeof (uint16_t ));
1017
- memcpy (&CompressionMethod, Blob.data () + MagicSize + VersionFieldSize,
1018
- sizeof (uint16_t ));
1019
- memcpy (&UncompressedSize,
1020
- Blob.data () + MagicSize + VersionFieldSize + MethodFieldSize,
1021
- sizeof (uint32_t ));
1022
- memcpy (&StoredHash,
1023
- Blob.data () + MagicSize + VersionFieldSize + MethodFieldSize +
1024
- SizeFieldSize,
1025
- sizeof (uint64_t ));
1086
+ memcpy (&StoredHash, Blob.data () + CurrentOffset, sizeof (uint64_t ));
1087
+ CurrentOffset += HashFieldSize;
1026
1088
1027
1089
llvm::compression::Format CompressionFormat;
1028
1090
if (CompressionMethod ==
@@ -1041,7 +1103,7 @@ CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
1041
1103
DecompressTimer.startTimer ();
1042
1104
1043
1105
SmallVector<uint8_t , 0 > DecompressedData;
1044
- StringRef CompressedData = Blob.substr (HeaderSize );
1106
+ StringRef CompressedData = Blob.substr (CurrentOffset );
1045
1107
if (llvm::Error DecompressionError = llvm::compression::decompress (
1046
1108
CompressionFormat, llvm::arrayRefFromStringRef (CompressedData),
1047
1109
DecompressedData, UncompressedSize))
@@ -1052,7 +1114,10 @@ CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
1052
1114
if (Verbose) {
1053
1115
DecompressTimer.stopTimer ();
1054
1116
1055
- // Recalculate MD5 hash
1117
+ double DecompressionTimeSeconds =
1118
+ DecompressTimer.getTotalTime ().getWallTime ();
1119
+
1120
+ // Recalculate MD5 hash for integrity check
1056
1121
llvm::Timer HashRecalcTimer (" Hash Recalculation Timer" ,
1057
1122
" Hash recalculation time" ,
1058
1123
ClangOffloadBundlerTimerGroup);
@@ -1066,16 +1131,30 @@ CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
1066
1131
HashRecalcTimer.stopTimer ();
1067
1132
bool HashMatch = (StoredHash == RecalculatedHash);
1068
1133
1069
- llvm::errs () << " Compressed bundle format version: " << ThisVersion << " \n "
1070
- << " Decompression method: "
1134
+ double CompressionRate =
1135
+ static_cast <double >(UncompressedSize) / CompressedData.size ();
1136
+ double DecompressionSpeedMBs =
1137
+ (UncompressedSize / (1024.0 * 1024.0 )) / DecompressionTimeSeconds;
1138
+
1139
+ llvm::errs () << " Compressed bundle format version: " << ThisVersion << " \n " ;
1140
+ if (ThisVersion >= 2 )
1141
+ llvm::errs () << " Total file size (from header): "
1142
+ << formatWithCommas (TotalFileSize) << " bytes\n " ;
1143
+ llvm::errs () << " Decompression method: "
1071
1144
<< (CompressionFormat == llvm::compression::Format::Zlib
1072
1145
? " zlib"
1073
1146
: " zstd" )
1074
1147
<< " \n "
1075
- << " Size before decompression: " << CompressedData.size ()
1076
- << " bytes\n "
1077
- << " Size after decompression: " << UncompressedSize
1078
- << " bytes\n "
1148
+ << " Size before decompression: "
1149
+ << formatWithCommas (CompressedData.size ()) << " bytes\n "
1150
+ << " Size after decompression: "
1151
+ << formatWithCommas (UncompressedSize) << " bytes\n "
1152
+ << " Compression rate: "
1153
+ << llvm::format (" %.2lf" , CompressionRate) << " \n "
1154
+ << " Compression ratio: "
1155
+ << llvm::format (" %.2lf%%" , 100.0 / CompressionRate) << " \n "
1156
+ << " Decompression speed: "
1157
+ << llvm::format (" %.2lf MB/s" , DecompressionSpeedMBs) << " \n "
1079
1158
<< " Stored hash: " << llvm::format_hex (StoredHash, 16 ) << " \n "
1080
1159
<< " Recalculated hash: "
1081
1160
<< llvm::format_hex (RecalculatedHash, 16 ) << " \n "
@@ -1269,8 +1348,10 @@ Error OffloadBundler::BundleFiles() {
1269
1348
std::unique_ptr<llvm::MemoryBuffer> BufferMemory =
1270
1349
llvm::MemoryBuffer::getMemBufferCopy (
1271
1350
llvm::StringRef (Buffer.data (), Buffer.size ()));
1272
- auto CompressionResult =
1273
- CompressedOffloadBundle::compress (*BufferMemory, BundlerConfig.Verbose );
1351
+ auto CompressionResult = CompressedOffloadBundle::compress (
1352
+ {BundlerConfig.CompressionFormat , BundlerConfig.CompressionLevel ,
1353
+ /* zstdEnableLdm=*/ true },
1354
+ *BufferMemory, BundlerConfig.Verbose );
1274
1355
if (auto Error = CompressionResult.takeError ())
1275
1356
return Error;
1276
1357
0 commit comments