Как уменьшить вызова метода padnumber для оптимизации?
здесь я реализую вывод чисел одновременно в несколько файлов из нескольких потоков, и вот код, который генерирует номер автомобиля, в этом коде я должен его оптимизировать. Сначала я попробовал использовать Stringbuilder вне цикла, но это не улучшило производительность, а затем мне посоветовали уменьшить количество вызовов метода padNumber (пример уменьшения количества вызовов метода padNumber для регионов), что может помочь в повышении быстродействия
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Loader implements Runnable{
private int i;
public Loader(int i)
{
this.i=i;
}
@Override
public void run() {
long start = System.currentTimeMillis();
try {
PrintWriter writer = new PrintWriter("res/NUMBERS " +i + ".txt");
char letters[] = {'У', 'К', 'Е', 'Н', 'Х', 'В', 'А', 'Р', 'О', 'С', 'М', 'Т'};
for (int regionCode = 1; regionCode <= 199; regionCode++) {
StringBuilder stringBuilder = new StringBuilder();
for (int number = 1; number < 1000; number++) {
for (char firstLetter : letters) {
for (char secondLetter : letters) {
for (char thirdLetter : letters) {
stringBuilder.append(firstLetter)
.append(padNumber(number, 3))
.append(secondLetter)
.append(thirdLetter)
.append(padNumber(regionCode, 2))
.append("\n");
}
}
}
}
writer.write(stringBuilder.toString());
}
writer.flush();
writer.close();
System.out.println(System.currentTimeMillis() - start);
} catch (IOException e) {
e.printStackTrace();
}
}
private static String padNumber(int number, int numberLength) {
String numberStr = Integer.toString(number);
int padSize = numberLength - numberStr.length();
for(int i=0;i<padSize;i++) {
numberStr = '0' + numberStr;
}
return numberStr;
}
public static void main(String[] args) {
int numOfThreads = 2;
ExecutorService executorService = Executors.newFixedThreadPool(2);
for(int i=0;i<numOfThreads;i++)
{
executorService.execute(new Loader(i));
}
executorService.shutdown();
try {
executorService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
текущая скорость в этом коде для каждого файла составляет 60 секунд
Ответы (1 шт):
Разумеется, следует закешировать результаты вызовов padNumber в соответствующих циклах верхнего уровня.
Однако и сам метод padNumber следует исправить, чтобы он также использовал StringBuilder, а не конкатенацию в цикле. Более того, в данном коде можно создавать и переиспользовать только один экземпляр StringBuilder заранее известной ёмкости, который можно очищать при помощи метода setLength.
Также не лишним будет напомнить о существовании конструкции try-with-resources, которая реализует автоматическое закрытие потоков ввода/вывода.
Дополнение
Имеет смысл использовать буферизированную запись при помощи BufferedWriter для повышения производительности, см. javadoc BufferedWriter.
Тогда также придётся создать экземпляр FileWriter, для непосредственной записи в файл (конструктор BufferedWriter принимает экземпяр Writer, а не строку-название файла / файл File).
Исправленный код может выглядеть так (не тестировался):
@Override
public void run() {
long start = System.currentTimeMillis();
try (PrintWriter writer = new PrintWriter(new BufferedWriter(
new FileWriter("res/NUMBERS_" + i + ".txt")
))
) {
char[] letters = {'У', 'К', 'Е', 'Н', 'Х', 'В', 'А', 'Р', 'О', 'С', 'М', 'Т'};
StringBuilder sb = new StringBuilder(10 * letters.length * letters.length * letters.length); // максимальный размер буфера
for (int regionCode = 1; regionCode <= 199; regionCode++) {
sb.setLength(0); // очистка буфера
// генерация кода региона, 2-3 цифры
String region = padNumber(regionCode, 2, sb);
for (int number = 1; number < 1000; number++) {
sb.setLength(0); // очистка буфера
// генерация трёхзначного номера
String num = padNumber(number, 3, sb);
sb.setLength(0);
for (char firstLetter : letters) {
for (char secondLetter : letters) {
for (char thirdLetter : letters) {
sb
.append(firstLetter)
.append(num)
.append(secondLetter)
.append(thirdLetter)
.append(region)
.append('\n');
}
}
}
// печать
writer.print(sb);
}
}
System.out.println(System.currentTimeMillis() - start);
} catch (IOException ioex) {
ioex.printStackTrace();
}
}
private static String padNumber(int number, int numberLength, StringBuilder sb) {
sb.append(number);
while (sb.length() < numberLength) {
sb.insert(0, '0');
}
return sb.toString();
}