Производительность ввода-вывода между Go и Java - PullRequest
0 голосов
/ 24 декабря 2018

У меня был простой тест производительности между go (1.11) и java (1.8) на моем Mac (версия Majave) с памятью 4Cpus / i5 и 16G, я обнаружил, что при чтении небольшого файла golang работает в 6 ~ 7 раз быстреечем Java.Ниже приведен мой тестовый код, я хочу подтвердить, является ли мой тестовый код неправильным или я что-то пропустил?

  1. Java

с concurrent.ExecutorService

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;



class TaskWithResult implements Callable<String> {
        private static String readToString() {
        String fileName = "/Users/pis/IdeaProjects/Test/src/data/test.txt";
        File file = new File(fileName);
        Long filelength = file.length();
        byte[] filecontent = new byte[filelength.intValue()];
        try {
            FileInputStream in = new FileInputStream(file);
            in.read(filecontent);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        SimpleDateFormat myFmt=new SimpleDateFormat("yyyy-MM-dd HH: mm: ss: SSS: ");
        Date d1 = new Date();
        return myFmt.format(d1);
    }

    /**
     * 任务的具体过程,一旦任务传给ExecutorService的submit方法,
     * 则该方法自动在一个线程上执行
     */
    public String call() throws Exception {
        String result = readToString();
        System.out.println(result);
        //该返回结果将被Future的get方法得到
        return result;
    }
}


public class readFile{
    public static void main(String args[]){
        ExecutorService es = Executors.newFixedThreadPool(5);
        List<Future<String>> resultList = new ArrayList<Future<String>>();
        SimpleDateFormat myFmt=new SimpleDateFormat("yyyy-MM-dd HH: mm: ss: SSS");
        Date d1 = new Date();
        System.out.println("Start Time:"+myFmt.format(d1));
        for (int i = 0; i < 1000; i++){
            //使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
            Future<String> future = es.submit(new TaskWithResult());
            //将任务执行结果存储到List中
            resultList.add(future);
        }
    }

}
Go

с каналом

package main

import (
    "fmt"
    "io/ioutil"
    "time"
)

func readFile(fileName string, p chan string)chan string {
    f, err := ioutil.ReadFile(fileName)
    if err!=nil{
        fmt.Println("read file error")
    }
    p<-string(f)
    return p
}

func main() {
    le := 1000
    p := make(chan string, le)
    start := time.Now()
    for i:=0;i<le;i++{
        go readFile("test.txt", p)
    }
    fmt.Println(fmt.Sprintf("Start Time: %s", start))
    for i:=0;i<le;i++{
        <-p
        fmt.Println(fmt.Sprintf("End Time: %s, duration: %f", time.Now(), time.Since(start).Seconds()))
    }

}
  • Результат

    • Go: завершить все задачи за 0,0197 с

      Время запуска: 2018-12-24 15: 30: 50.333694 +0800 CST m = + 0,000325519

      ...

      Время окончания

      ...

      Время окончания: 2018-12-24 15: 30: 50.353409 +0800 CST m = + 0.020040254, продолжительность: 0,019715

    • Java: завершить все задачи примерно за 122 мс

      Время запуска: 2018-12-24 15: 30: 31: 301

      ...

      2018-12-24 15: 30: 31: 422

Мой файл тестовых данных представляет собой очень простой текст в несколько строк(около 362В).Что-то не так с моим тестовым кодом в тестовом чтении небольшого файла между go и java?Кто-нибудь, пожалуйста, помогите мне.Заранее спасибо:)

1 Ответ

0 голосов
/ 24 декабря 2018

Я вижу несколько проблем с этим, как с концептуальной точки зрения, так и с технической.

Вы используете канал для возврата набора результатов (хорошо, вроде), но затем вы простовыбросить результат.Кроме того, вы используете небуферизованный канал, поэтому у вас есть задушевная точка.Обратите внимание, что это не проблема, так как конвейеры являются отличным способом структурирования вашей программы - вы просто неправильно использовали ее здесь, imho.Что-то в строке

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    le := 1000

    // We want to wait until the operations finish
    var wg sync.WaitGroup

    // We "prealloc" err, since we do not want le * allocations
    var err error


    start := time.Now()
    for i := 0; i < le; i++ {

        // Add an operation to wait for to the group
        wg.Add(1)
        go func() {
            // Ensure the WaitGroup is notified we are done (bar a panic)
            defer wg.Done()

            // Short notation, since we are not interested in the result set
            if _,err = ioutil.ReadFile(fileName);err!=nil{
             fmt.Println("read file error")
            }

        }()

    }

    // Wait until all operations are finished.
    wg.Wait()
    fmt.Printf("%d iterations took %s", le, time.Since(start))
}

было бы моим решением.Если бы у меня была идея сделать что-то вроде этого.

Но если мы углубимся в код, в основном, единственный работающий компонент здесь - ioutil.ReadFile.Использование этого для программных частей, которые заслуживают сравнительного анализа, - это в первую очередь Very Bad Idea ™.Его следует использовать для довольно небольших файлов (например, файлов конфигурации), что само по себе не является частью вашей программы, которую вы хотите тестировать.

То, что вы делаете хотитебенчмарк - это логика обработки файлов, которые вы только что прочитали.Позвольте мне привести пример: скажем, вы хотите прочитать несколько небольших файлов JSON, разобрать их, изменить, снова маршалировать и отправить в REST API.Итак, какую часть вашей программы вы бы хотели сравнить в этом случае?Моя ставка идет на логику обработки файлов.Потому что это часть программы, которую вы можете на самом деле оптимизировать.Вы не можете ни оптимизировать ioutil.ReadFile, ни сервер.Если вам не случится написать это тоже.В этом случае вы захотите сравнить логику сервера из пакета сервера.

И, наконец, ваш вопрос называется «Производительность ввода-вывода между Go и Java».Чтобы реально измерить производительность ввода-вывода, вам понадобятся очень большие операции ввода-вывода.Я склонен использовать ISO-образы для этого - данные реального мира, которые я обычно собираю.

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