[MNG-7899] Various memory usage improvements 4-1 (#1296)

* [MNG-7899] Various memory usage improvements

- BatchModeMavenTransferListener removed
- FileSizeFormat moved to top level class
- FileSizeFormat.formatProcess() refactored to accept an StringBuilder as argument
- FileSizeFormat refactored format() methods
- FileSizeFormat refactored, replace DecimalFormat with Math logic to
reduce memory allocation

---------

Co-authored-by: Guillaume Nodet <gnodet@gmail.com>
This commit is contained in:
sebastien-doyon 2023-11-10 00:47:54 +01:00 committed by GitHub
parent 4bd12915c9
commit 94749041b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 190 additions and 188 deletions

View File

@ -19,8 +19,6 @@
package org.apache.maven.cli.transfer; package org.apache.maven.cli.transfer;
import java.io.PrintStream; import java.io.PrintStream;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale; import java.util.Locale;
import org.apache.maven.cli.jansi.MessageUtils; import org.apache.maven.cli.jansi.MessageUtils;
@ -38,153 +36,6 @@ public abstract class AbstractMavenTransferListener extends AbstractTransferList
private static final String ANSI_DARK_SET = ESC + "[90m"; private static final String ANSI_DARK_SET = ESC + "[90m";
private static final String ANSI_DARK_RESET = ESC + "[0m"; private static final String ANSI_DARK_RESET = ESC + "[0m";
// CHECKSTYLE_OFF: LineLength
/**
* Formats file size with the associated <a href="https://en.wikipedia.org/wiki/Metric_prefix">SI</a> prefix
* (GB, MB, kB) and using the patterns <code>#0.0</code> for numbers between 1 and 10
* and <code>###0</code> for numbers between 10 and 1000+ by default.
*
* @see <a href="https://en.wikipedia.org/wiki/Metric_prefix">https://en.wikipedia.org/wiki/Metric_prefix</a>
* @see <a href="https://en.wikipedia.org/wiki/Binary_prefix">https://en.wikipedia.org/wiki/Binary_prefix</a>
* @see <a
* href="https://en.wikipedia.org/wiki/Octet_%28computing%29">https://en.wikipedia.org/wiki/Octet_(computing)</a>
*/
// CHECKSTYLE_ON: LineLength
// TODO Move me to Maven Shared Utils
static class FileSizeFormat {
enum ScaleUnit {
BYTE {
@Override
public long bytes() {
return 1L;
}
@Override
public String symbol() {
return "B";
}
},
KILOBYTE {
@Override
public long bytes() {
return 1000L;
}
@Override
public String symbol() {
return "kB";
}
},
MEGABYTE {
@Override
public long bytes() {
return KILOBYTE.bytes() * KILOBYTE.bytes();
}
@Override
public String symbol() {
return "MB";
}
},
GIGABYTE {
@Override
public long bytes() {
return MEGABYTE.bytes() * KILOBYTE.bytes();
}
;
@Override
public String symbol() {
return "GB";
}
};
public abstract long bytes();
public abstract String symbol();
public static ScaleUnit getScaleUnit(long size) {
if (size < 0L) {
throw new IllegalArgumentException("file size cannot be negative: " + size);
}
if (size >= GIGABYTE.bytes()) {
return GIGABYTE;
} else if (size >= MEGABYTE.bytes()) {
return MEGABYTE;
} else if (size >= KILOBYTE.bytes()) {
return KILOBYTE;
} else {
return BYTE;
}
}
}
private DecimalFormat smallFormat;
private DecimalFormat largeFormat;
FileSizeFormat(Locale locale) {
smallFormat = new DecimalFormat("#0.0", new DecimalFormatSymbols(locale));
largeFormat = new DecimalFormat("###0", new DecimalFormatSymbols(locale));
}
public String format(long size) {
return format(size, null);
}
public String format(long size, ScaleUnit unit) {
return format(size, unit, false);
}
@SuppressWarnings("checkstyle:magicnumber")
public String format(long size, ScaleUnit unit, boolean omitSymbol) {
if (size < 0L) {
throw new IllegalArgumentException("file size cannot be negative: " + size);
}
if (unit == null) {
unit = ScaleUnit.getScaleUnit(size);
}
double scaledSize = (double) size / unit.bytes();
String scaledSymbol = " " + unit.symbol();
if (omitSymbol) {
scaledSymbol = "";
}
if (unit == ScaleUnit.BYTE) {
return largeFormat.format(size) + scaledSymbol;
}
if (scaledSize < 0.05 || scaledSize >= 10.0) {
return largeFormat.format(scaledSize) + scaledSymbol;
} else {
return smallFormat.format(scaledSize) + scaledSymbol;
}
}
public String formatProgress(long progressedSize, long size) {
if (progressedSize < 0L) {
throw new IllegalArgumentException("progressed file size cannot be negative: " + size);
}
if (size >= 0 && progressedSize > size) {
throw new IllegalArgumentException(
"progressed file size cannot be greater than size: " + progressedSize + " > " + size);
}
if (size >= 0L && progressedSize != size) {
ScaleUnit unit = ScaleUnit.getScaleUnit(size);
String formattedProgressedSize = format(progressedSize, unit, true);
String formattedSize = format(size, unit);
return formattedProgressedSize + "/" + formattedSize;
} else {
return format(progressedSize);
}
}
}
protected PrintStream out; protected PrintStream out;
protected AbstractMavenTransferListener(PrintStream out) { protected AbstractMavenTransferListener(PrintStream out) {
@ -234,12 +85,15 @@ public abstract class AbstractMavenTransferListener extends AbstractTransferList
message.append(darkOff).append(resource.getRepositoryId()); message.append(darkOff).append(resource.getRepositoryId());
message.append(darkOn).append(": ").append(resource.getRepositoryUrl()); message.append(darkOn).append(": ").append(resource.getRepositoryUrl());
message.append(darkOff).append(resource.getResourceName()); message.append(darkOff).append(resource.getResourceName());
message.append(darkOn).append(" (").append(format.format(contentLength)); message.append(darkOn).append(" (");
format.format(message, contentLength);
long duration = System.currentTimeMillis() - resource.getTransferStartTime(); long duration = System.currentTimeMillis() - resource.getTransferStartTime();
if (duration > 0L) { if (duration > 0L) {
double bytesPerSecond = contentLength / (duration / 1000.0); double bytesPerSecond = contentLength / (duration / 1000.0);
message.append(" at ").append(format.format((long) bytesPerSecond)).append("/s"); message.append(" at ");
format.format(message, (long) bytesPerSecond);
message.append("/s");
} }
message.append(')').append(darkOff); message.append(')').append(darkOff);

View File

@ -1,30 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli.transfer;
import java.io.PrintStream;
/**
* BatchModeMavenTransferListener
*/
public class BatchModeMavenTransferListener extends AbstractMavenTransferListener {
public BatchModeMavenTransferListener(PrintStream out) {
super(out);
}
}

View File

@ -87,7 +87,7 @@ public class ConsoleMavenTransferListener extends AbstractMavenTransferListener
buffer.append(" ("); buffer.append(" (");
} }
buffer.append(format.formatProgress(complete, total)); format.formatProgress(buffer, complete, total);
if (printResourceNames) { if (printResourceNames) {
buffer.append(")"); buffer.append(")");

View File

@ -0,0 +1,176 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli.transfer;
import java.util.Locale;
/**
* Formats file size with the associated <a href="https://en.wikipedia.org/wiki/Metric_prefix">SI</a> prefix
* (GB, MB, kB) and using the patterns <code>#0.0</code> for numbers between 1 and 10
* and <code>###0</code> for numbers between 10 and 1000+ by default.
*
* @see <a href="https://en.wikipedia.org/wiki/Metric_prefix">https://en.wikipedia.org/wiki/Metric_prefix</a>
* @see <a href="https://en.wikipedia.org/wiki/Binary_prefix">https://en.wikipedia.org/wiki/Binary_prefix</a>
* @see <a
* href="https://en.wikipedia.org/wiki/Octet_%28computing%29">https://en.wikipedia.org/wiki/Octet_(computing)</a>
*/
public class FileSizeFormat {
public enum ScaleUnit {
BYTE {
@Override
public long bytes() {
return 1L;
}
@Override
public String symbol() {
return "B";
}
},
KILOBYTE {
@Override
public long bytes() {
return 1000L;
}
@Override
public String symbol() {
return "kB";
}
},
MEGABYTE {
@Override
public long bytes() {
return KILOBYTE.bytes() * KILOBYTE.bytes();
}
@Override
public String symbol() {
return "MB";
}
},
GIGABYTE {
@Override
public long bytes() {
return MEGABYTE.bytes() * KILOBYTE.bytes();
}
;
@Override
public String symbol() {
return "GB";
}
};
public abstract long bytes();
public abstract String symbol();
public static ScaleUnit getScaleUnit(long size) {
if (size < 0L) {
throw new IllegalArgumentException("file size cannot be negative: " + size);
}
if (size >= GIGABYTE.bytes()) {
return GIGABYTE;
} else if (size >= MEGABYTE.bytes()) {
return MEGABYTE;
} else if (size >= KILOBYTE.bytes()) {
return KILOBYTE;
} else {
return BYTE;
}
}
}
public FileSizeFormat(Locale locale) {}
public String format(long size) {
return format(size, null);
}
public String format(long size, ScaleUnit unit) {
return format(size, unit, false);
}
public String format(long size, ScaleUnit unit, boolean omitSymbol) {
StringBuilder sb = new StringBuilder();
format(sb, size, unit, omitSymbol);
return sb.toString();
}
public void format(StringBuilder builder, long size) {
format(builder, size, null, false);
}
public void format(StringBuilder builder, long size, ScaleUnit unit) {
format(builder, size, unit, false);
}
@SuppressWarnings("checkstyle:magicnumber")
private void format(StringBuilder builder, long size, ScaleUnit unit, boolean omitSymbol) {
if (size < 0L) {
throw new IllegalArgumentException("file size cannot be negative: " + size);
}
if (unit == null) {
unit = ScaleUnit.getScaleUnit(size);
}
double scaledSize = (double) size / unit.bytes();
if (unit == ScaleUnit.BYTE) {
builder.append(size);
} else if (scaledSize < 0.05d || scaledSize >= 10.0d) {
builder.append(Math.round(scaledSize));
} else {
builder.append(Math.round(scaledSize * 10d) / 10d);
}
if (!omitSymbol) {
builder.append(" ").append(unit.symbol());
}
}
public String formatProgress(long progressedSize, long size) {
StringBuilder sb = new StringBuilder();
formatProgress(sb, progressedSize, size);
return sb.toString();
}
public void formatProgress(StringBuilder builder, long progressedSize, long size) {
if (progressedSize < 0L) {
throw new IllegalArgumentException("progressed file size cannot be negative: " + size);
}
if (size >= 0 && progressedSize > size) {
throw new IllegalArgumentException(
"progressed file size cannot be greater than size: " + progressedSize + " > " + size);
}
if (size >= 0L && progressedSize != size) {
ScaleUnit unit = ScaleUnit.getScaleUnit(size);
format(builder, progressedSize, unit, true);
builder.append("/");
format(builder, size, unit, false);
} else {
ScaleUnit unit = ScaleUnit.getScaleUnit(progressedSize);
format(builder, progressedSize, unit, false);
}
}
}

View File

@ -20,7 +20,6 @@ package org.apache.maven.cli.transfer;
import java.util.Locale; import java.util.Locale;
import org.apache.maven.cli.transfer.AbstractMavenTransferListener.FileSizeFormat;
import org.eclipse.aether.transfer.AbstractTransferListener; import org.eclipse.aether.transfer.AbstractTransferListener;
import org.eclipse.aether.transfer.TransferCancelledException; import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.transfer.TransferEvent; import org.eclipse.aether.transfer.TransferEvent;
@ -81,13 +80,17 @@ public class Slf4jMavenTransferListener extends AbstractTransferListener {
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
message.append(action).append(' ').append(direction).append(' ').append(resource.getRepositoryId()); message.append(action).append(' ').append(direction).append(' ').append(resource.getRepositoryId());
message.append(": "); message.append(": ");
message.append(resource.getRepositoryUrl()).append(resource.getResourceName()); message.append(resource.getRepositoryUrl())
message.append(" (").append(format.format(contentLength)); .append(resource.getResourceName())
.append(" (");
format.format(message, contentLength);
long duration = System.currentTimeMillis() - resource.getTransferStartTime(); long duration = System.currentTimeMillis() - resource.getTransferStartTime();
if (duration > 0L) { if (duration > 0L) {
double bytesPerSecond = contentLength / (duration / 1000.0); double bytesPerSecond = contentLength / (duration / 1000.0);
message.append(" at ").append(format.format((long) bytesPerSecond)).append("/s"); message.append(" at ");
format.format(message, (long) bytesPerSecond);
message.append("/s");
} }
message.append(')'); message.append(')');

View File

@ -20,8 +20,7 @@ package org.apache.maven.cli.transfer;
import java.util.Locale; import java.util.Locale;
import org.apache.maven.cli.transfer.AbstractMavenTransferListener.FileSizeFormat; import org.apache.maven.cli.transfer.FileSizeFormat.ScaleUnit;
import org.apache.maven.cli.transfer.AbstractMavenTransferListener.FileSizeFormat.ScaleUnit;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;