Есть ли лучший способ для быстрого создания 5 миллионов CSV-файлов - PullRequest
0 голосов
/ 08 апреля 2020

Я хотел бы создать 5 миллионов CSV-файлов, я жду почти 3 часа, но программа все еще работает. Может кто-нибудь дать мне совет, как ускорить генерацию файлов.

После завершения генерации этих 5 миллионов файлов я должен загрузить их в корзину s3.

Было бы лучше, если бы кто-то знал, как генерировать эти файлы с помощью AWS, таким образом, мы можем Переместите файлы в корзину s3 напрямую и не обращайте внимания на проблему скорости сети. (Просто начните изучать AWS, нужно знать много знаний)

Ниже приведен мой код.

public class ParallelCsvGenerate implements Runnable {
    private static AtomicLong baseID = new AtomicLong(8160123456L);
    private static ThreadLocalRandom random = ThreadLocalRandom.current();
    private static ThreadLocalRandom random2 = ThreadLocalRandom.current();
    private static String filePath = "C:\\5millionfiles\\";
    private static List<String> headList = null;
    private static String csvHeader = null;
    public ParallelCsvGenerate() {
        headList = generateHeadList();
        csvHeader = String.join(",", headList);
    }


    @Override
    public void run() {
        for(int i = 0; i < 1000000; i++) {
            generateCSV();
        }s
    }


    private void generateCSV() {
        StringBuilder builder = new StringBuilder();
        builder.append(csvHeader).append(System.lineSeparator());
        for (int i = 0; i < headList.size(); i++) {
            if(i < headList.size() - 1) {
                builder.append(i % 2 == 0 ? generateRandomInteger() : generateRandomStr()).append(",");
            } else {
                builder.append(i % 2 == 0 ? generateRandomInteger() : generateRandomStr());
            }
        }


        String fileName = String.valueOf(baseID.addAndGet(1));
        File csvFile = new File(filePath + fileName + ".csv");
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter(csvFile);
            fileWriter.write(builder.toString());
            fileWriter.flush();
        } catch (Exception e) {
            System.err.println(e);
        } finally {
            try {
                if(fileWriter != null) {
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }




    private static List<String> generateHeadList() {
        List<String> headList = new ArrayList<>(20);
        String baseFiledName = "Field";
        for(int i = 1; i <=20; i++) {
            headList.add(baseFiledName + i);
        }
        return headList;
    }




    /**
     * generate a number in range of 0-50000
     * @return
     */
    private Integer generateRandomInteger() {
        return random.nextInt(0,50000);
    }




    /**
     * generate a string length is 5 - 8
     * @return
     */
    private String generateRandomStr() {
        int strLength = random2.nextInt(5, 8);
        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        int length = str.length();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < strLength; i++) {
            builder.append(str.charAt(random.nextInt(length)));
        }
        return builder.toString();
    }

Main

ParallelCsvGenerate generate = new ParallelCsvGenerate();


Thread a = new Thread(generate, "A");
Thread b = new Thread(generate, "B");
Thread c = new Thread(generate, "C");
Thread d = new Thread(generate, "D");
Thread e = new Thread(generate, "E");

a.run();
b.run();
c.run();
d.run();
e.run();

Спасибо за советы, ребята, просто проведите рефакторинг кода и сгенерируйте 3,8 миллиона файлов, используя 2,8 часа, что намного лучше. Код рефакторинга:

public class ParallelCsvGenerate implements Callable<Integer> {
    private static String filePath = "C:\\5millionfiles\\";
    private static String[] header = new String[]{
            "FIELD1","FIELD2","FIELD3","FIELD4","FIELD5",
            "FIELD6","FIELD7","FIELD8","FIELD9","FIELD10",
            "FIELD11","FIELD12","FIELD13","FIELD14","FIELD15",
            "FIELD16","FIELD17","FIELD18","FIELD19","FIELD20",
    };
    private String fileName;
    public ParallelCsvGenerate(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public Integer call() throws Exception {
        try {
            generateCSV();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }

    private void generateCSV() throws IOException {

        CSVWriter writer = new CSVWriter(new FileWriter(filePath + fileName + ".csv"), CSVWriter.DEFAULT_SEPARATOR, CSVWriter.NO_QUOTE_CHARACTER);
        String[] content = new String[]{
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr(),
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr(),
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr(),
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr(),
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr(),
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr(),
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr(),
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr(),
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr(),
                RandomGenerator.generateRandomInteger(),
                RandomGenerator.generateRandomStr()
        };
        writer.writeNext(header);
        writer.writeNext(content);
        writer.close();
    }

}

Основной

public static void main(String[] args) {
        System.out.println("Start generate");
        long start = System.currentTimeMillis();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 8,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
        List<ParallelCsvGenerate> taskList = new ArrayList<>(3800000);
        for(int i = 0; i < 3800000; i++) {
            taskList.add(new ParallelCsvGenerate(i+""));
        }
        try {
            List<Future<Integer>> futures = threadPoolExecutor.invokeAll(taskList);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Success");
        long end = System.currentTimeMillis();
        System.out.println("Using time: " + (end-start));
    }

Ответы [ 3 ]

0 голосов
/ 08 апреля 2020

Вы можете использовать инфраструктуру Fork / Join (java 7 и выше), чтобы сделать ваш процесс параллельным и использовать многоядерный процессор. Я возьму для вас пример.

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;

public class ForkJoinAdd extends RecursiveTask<Long> {

    private final long[] numbers;
    private final int start;
    private final int end;
    public static final long threshold = 10_000;

    public ForkJoinAdd(long[] numbers) {
        this(numbers, 0, numbers.length);
    }

    private ForkJoinAdd(long[] numbers, int start, int end) {
        this.numbers = numbers;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {

        int length = end - start;
        if (length <= threshold) {
            return add();
        }

        ForkJoinAdd firstTask = new ForkJoinAdd(numbers, start, start + length / 2);
        firstTask.fork(); //start asynchronously

        ForkJoinAdd secondTask = new ForkJoinAdd(numbers, start + length / 2, end);

        Long secondTaskResult = secondTask.compute();
        Long firstTaskResult = firstTask.join();

        return firstTaskResult + secondTaskResult;

    }

    private long add() {
        long result = 0;
        for (int i = start; i < end; i++) {
            result += numbers[i];
        }
        return result;
    }

    public static long startForkJoinSum(long n) {
        long[] numbers = LongStream.rangeClosed(1, n).toArray();
        ForkJoinTask<Long> task = new ForkJoinAdd(numbers);
        return new ForkJoinPool().invoke(task);
    }

}

используйте этот пример И если вы хотите узнать больше об этом, Руководство по Fork / Join Framework в Java | Baeldung и Fork / Join (Учебные руководства Java ™ могут помочь вам лучше понять и лучше спроектировать ваше приложение. Будьте счастливы.

0 голосов
/ 08 апреля 2020
  • Удалите метод for(int i = 0; i < 1000000; i++) l oop из run (оставьте один вызов generateCSV().
  • Создайте 5 миллионов ParallelCsvGenerate объектов.
  • Отправить их в ThreadPoolExecutor

Преобразовано main:

public static void main(String[] args) {    
    ThreadPoolExecutor ex = (ThreadPoolExecutor) Executors.newFixedThreadPool(8);
    for(int i = 0; i < 5000000; i++) {
        ParallelCsvGenerate generate = new ParallelCsvGenerate();
        ex.submit(generate);
    }
    ex.shutdown();
}

На моем ноутбуке требуется примерно 5 минут (4 физических ядра с гиперпоточностью, SSD-накопитель).

РЕДАКТИРОВАТЬ:

Я заменил FileWriter на AsynchronousFileChannel, используя следующий код:

    Path file = Paths.get(filePath + fileName + ".csv");
    try(AsynchronousFileChannel asyncFile = AsynchronousFileChannel.open(file,
                        StandardOpenOption.WRITE,
                        StandardOpenOption.CREATE)) {

        asyncFile.write(ByteBuffer.wrap(builder.toString().getBytes()), 0);
    } catch (IOException e) {
        e.printStackTrace();
    }

для достижения 30% ускорения .

Я считаю, что основным узким местом является жесткий диск и сама файловая система. Здесь можно сделать гораздо больше.

0 голосов
/ 08 апреля 2020
  1. Вы можете записать непосредственно в файл (без выделения всего файла в одном StringBuilder). (Я думаю, что это самое большое время + память узкое место здесь: builder.toString())
  2. Вы можете генерировать каждый файл параллельно.
  3. (маленькие хитрости :) Пропустите if внутри l oop.

    if(i < headList.size() - 1) не требуется, когда вы делаете более умную l oop + 1 дополнительную итерацию.

    i % 2 == 0 можно устранить с помощью лучшей итерации (i+=2) ... и больше труда внутри l oop (i -> int, i + 1 -> string)

  4. Если применимо, предпочитайте от append(char) до append(String). (Лучше append(','), чем append(",")!)

...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...