programing

Java 파일 크기 가져오기

prostudy 2022. 5. 2. 20:30
반응형

Java 파일 크기 가져오기

구글링을 하면서, 나는 사용하는 것이 느릴 수 있다는 것을 알게 된다.FileChannel 또한 이용할 수 있는 방법을 가지고 있다.

Java에서 파일 크기를 효율적으로 얻을 수 있는 방법이 있는가?

음, 나는 아래 코드로 측정하려고 했다.

실행 = 1 및 반복 = 1의 경우 URL 방법이 채널 다음으로 가장 빠른 시간이다.나는 이것을 10번 정도 신선하게 잠시 멈추면서 실행한다.따라서 한 번 액세스해 볼 때 URL을 사용하는 것이 가장 빠를 수 있는 방법:

LENGTH sum: 10626, per Iteration: 10626.0

CHANNEL sum: 5535, per Iteration: 5535.0

URL sum: 660, per Iteration: 660.0

런 = 5 및 반복 = 50의 경우 그림이 서로 다르게 그려진다.

LENGTH sum: 39496, per Iteration: 157.984

CHANNEL sum: 74261, per Iteration: 297.044

URL sum: 95534, per Iteration: 382.136

파일이 파일 시스템에 대한 호출을 캐싱하고 있어야 하며, 채널과 URL에는 오버헤드가 있다.

코드:

import java.io.*;
import java.net.*;
import java.util.*;

public enum FileSizeBench {

    LENGTH {
        @Override
        public long getResult() throws Exception {
            File me = new File(FileSizeBench.class.getResource(
                    "FileSizeBench.class").getFile());
            return me.length();
        }
    },
    CHANNEL {
        @Override
        public long getResult() throws Exception {
            FileInputStream fis = null;
            try {
                File me = new File(FileSizeBench.class.getResource(
                        "FileSizeBench.class").getFile());
                fis = new FileInputStream(me);
                return fis.getChannel().size();
            } finally {
                fis.close();
            }
        }
    },
    URL {
        @Override
        public long getResult() throws Exception {
            InputStream stream = null;
            try {
                URL url = FileSizeBench.class
                        .getResource("FileSizeBench.class");
                stream = url.openStream();
                return stream.available();
            } finally {
                stream.close();
            }
        }
    };

    public abstract long getResult() throws Exception;

    public static void main(String[] args) throws Exception {
        int runs = 5;
        int iterations = 50;

        EnumMap<FileSizeBench, Long> durations = new EnumMap<FileSizeBench, Long>(FileSizeBench.class);

        for (int i = 0; i < runs; i++) {
            for (FileSizeBench test : values()) {
                if (!durations.containsKey(test)) {
                    durations.put(test, 0l);
                }
                long duration = testNow(test, iterations);
                durations.put(test, durations.get(test) + duration);
                // System.out.println(test + " took: " + duration + ", per iteration: " + ((double)duration / (double)iterations));
            }
        }

        for (Map.Entry<FileSizeBench, Long> entry : durations.entrySet()) {
            System.out.println();
            System.out.println(entry.getKey() + " sum: " + entry.getValue() + ", per Iteration: " + ((double)entry.getValue() / (double)(runs * iterations)));
        }

    }

    private static long testNow(FileSizeBench test, int iterations)
            throws Exception {
        long result = -1;
        long before = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            if (result == -1) {
                result = test.getResult();
                //System.out.println(result);
            } else if ((result = test.getResult()) != result) {
                 throw new Exception("variance detected!");
             }
        }
        return (System.nanoTime() - before) / 1000;
    }

}

GHAD가 제공하는 벤치마크는 길이를 얻는 것 외에 많은 다른 것(반사, 물체 인스턴스화 등)을 측정한다.만약 우리가 이런 것들을 없애려고 노력한다면 한 번의 통화로 나는 다음 시간을 마이크로초 단위로 얻을 수 있다.

파일 sum___19.0, Iteration__19.0라프섬___16.0, 반복당__16.0채널 sum_273.0, Iteration__273.0당

100회 주행 및 10000회 반복 시:

파일 sum_1767629.0, Iteration__1.7676290000000001당래프섬___881284.0, Iteration__0.88128400000001채널 sum___414286.0, Iteration__0.414286당

나는 100MB 파일의 이름을 인수로 주기 위해 다음과 같은 수정된 코드를 실행했다.

import java.io.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;

public class FileSizeBench {

  private static File file;
  private static FileChannel channel;
  private static RandomAccessFile raf;

  public static void main(String[] args) throws Exception {
    int runs = 1;
    int iterations = 1;

    file = new File(args[0]);
    channel = new FileInputStream(args[0]).getChannel();
    raf = new RandomAccessFile(args[0], "r");

    HashMap<String, Double> times = new HashMap<String, Double>();
    times.put("file", 0.0);
    times.put("channel", 0.0);
    times.put("raf", 0.0);

    long start;
    for (int i = 0; i < runs; ++i) {
      long l = file.length();

      start = System.nanoTime();
      for (int j = 0; j < iterations; ++j)
        if (l != file.length()) throw new Exception();
      times.put("file", times.get("file") + System.nanoTime() - start);

      start = System.nanoTime();
      for (int j = 0; j < iterations; ++j)
        if (l != channel.size()) throw new Exception();
      times.put("channel", times.get("channel") + System.nanoTime() - start);

      start = System.nanoTime();
      for (int j = 0; j < iterations; ++j)
        if (l != raf.length()) throw new Exception();
      times.put("raf", times.get("raf") + System.nanoTime() - start);
    }
    for (Map.Entry<String, Double> entry : times.entrySet()) {
        System.out.println(
            entry.getKey() + " sum: " + 1e-3 * entry.getValue() +
            ", per Iteration: " + (1e-3 * entry.getValue() / runs / iterations));
    }
  }
}

이 게시물의 모든 테스트 케이스는 테스트한 각 방법에 대해 동일한 파일에 액세스하기 때문에 결함이 있다.그래서 디스크 캐슁은 테스트 2와 테스트 3의 이점을 활용한다.내 요점을 증명하기 위해 나는 GHAD에서 제공한 테스트 케이스를 취했고 열거 순서를 변경했고 아래는 결과야.

결과를 보니 File.length()가 정말 승자라고 생각한다.

시험 순서는 출력 순서다.내 기계에 걸리는 시간도 파일만 빼면 다르잖아길이()는 처음이 아닐 때 첫 번째 디스크 액세스 권한을 발생시키는 경우.

---
LENGTH sum: 1163351, per Iteration: 4653.404
CHANNEL sum: 1094598, per Iteration: 4378.392
URL sum: 739691, per Iteration: 2958.764

---
CHANNEL sum: 845804, per Iteration: 3383.216
URL sum: 531334, per Iteration: 2125.336
LENGTH sum: 318413, per Iteration: 1273.652

--- 
URL sum: 137368, per Iteration: 549.472
LENGTH sum: 18677, per Iteration: 74.708
CHANNEL sum: 142125, per Iteration: 568.5

리소스 대신 절대 경로로 액세스한 파일을 사용하도록 코드를 수정하면 다른 결과(1회 실행, 1회 반복 및 10만 바이트 파일 - 10만 바이트 파일의 시간은 10만 바이트와 동일)를 얻는다.

길이 합: 33, 반복당: 33.0

채널 합계: 3626, 반복당: 3626.0

URL 합계: 294, 반복당: 294.0

rgrig의 벤치마크에 대응하여 FileChannel & RandomAccessFile 인스턴스를 열거나 닫는 데 걸리는 시간도 고려해야 하며, 이러한 클래스는 파일을 읽기 위한 스트림을 열 것이기 때문이다.

벤치마크를 수정한 후 85MB 파일에서 1회 반복하여 다음과 같은 결과를 얻었다.

file totalTime: 48000 (48 us)
raf totalTime: 261000 (261 us)
channel totalTime: 7020000 (7 ms)

동일한 파일에서 10000번 반복하는 경우:

file totalTime: 80074000 (80 ms)
raf totalTime: 295417000 (295 ms)
channel totalTime: 368239000 (368 ms)

파일 크기만 있으면 file.length()가 가장 빠른 방법이다.만약 당신이 그 파일을 읽기/쓰기 같은 다른 목적으로 사용할 계획이라면, RAF가 더 나은 선택인 것 같다.파일 연결을 닫는 것을 잊지 마십시오.

import java.io.File;
import java.io.FileInputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;

public class FileSizeBench
{    
    public static void main(String[] args) throws Exception
    {
        int iterations = 1;
        String fileEntry = args[0];

        Map<String, Long> times = new HashMap<String, Long>();
        times.put("file", 0L);
        times.put("channel", 0L);
        times.put("raf", 0L);

        long fileSize;
        long start;
        long end;
        File f1;
        FileChannel channel;
        RandomAccessFile raf;

        for (int i = 0; i < iterations; i++)
        {
            // file.length()
            start = System.nanoTime();
            f1 = new File(fileEntry);
            fileSize = f1.length();
            end = System.nanoTime();
            times.put("file", times.get("file") + end - start);

            // channel.size()
            start = System.nanoTime();
            channel = new FileInputStream(fileEntry).getChannel();
            fileSize = channel.size();
            channel.close();
            end = System.nanoTime();
            times.put("channel", times.get("channel") + end - start);

            // raf.length()
            start = System.nanoTime();
            raf = new RandomAccessFile(fileEntry, "r");
            fileSize = raf.length();
            raf.close();
            end = System.nanoTime();
            times.put("raf", times.get("raf") + end - start);
        }

        for (Map.Entry<String, Long> entry : times.entrySet()) {
            System.out.println(entry.getKey() + " totalTime: " + entry.getValue() + " (" + getTime(entry.getValue()) + ")");
        }
    }

    public static String getTime(Long timeTaken)
    {
        if (timeTaken < 1000) {
            return timeTaken + " ns";
        } else if (timeTaken < (1000*1000)) {
            return timeTaken/1000 + " us"; 
        } else {
            return timeTaken/(1000*1000) + " ms";
        } 
    }
}

나는 이 같은 문제에 부딪쳤다.나는 네트워크 공유에 있는 9만 개의 파일 크기와 수정 날짜를 얻어야 했다.자바를 사용하고, 가능한 한 미니멀리즘적이면, 아주 오랜 시간이 걸릴 것이다.(파일에서 URL을 가져와야 했고, 개체의 경로도 가져와야 했다.그래서 그것은 다소 다양했지만, 한 시간 이상이었다.)그리고 나서 나는 네이티브 Win32 실행 파일을 사용했고, 파일 경로만 덤핑하고, 수정하고, 크기를 콘솔에 넣고, 그것을 자바에서 실행했다.속도는 놀라웠다.데이터를 읽기 위한 기본 프로세스와 문자열 처리로 초당 1000개 이상의 항목을 처리할 수 있다.

그래서 비록 사람들이 위의 코멘트에 순위를 매겼지만, 이것은 유효한 해결책이고, 나의 문제를 해결했다.내 경우에는 미리 필요한 폴더 크기를 알고 있었고, 명령행에서 그것을 내 win32 앱으로 전달할 수 있었다.나는 몇 시간에서 몇 분으로 디렉토리를 처리했다.

그 이슈는 또한 윈도우 특유의 것처럼 보였다.OS X는 동일한 문제를 가지고 있지 않았고 OS가 그렇게 할 수 있는 한 빨리 네트워크 파일 정보에 접근할 수 있었다.

Windows에서 Java 파일을 처리하는 것은 끔찍하다.그러나 파일의 로컬 디스크 액세스는 괜찮다.그 끔찍한 실적을 일으킨 것은 단지 네트워크 공유에 불과했다.윈도우는 네트워크 공유에 대한 정보를 얻고 1분 안에 총 크기를 계산할 수도 있다.

--벤

디렉터리에 여러 파일의 파일 크기를 지정하려면 를 사용하십시오.사이즈는 에서 구할 수 있다.BasicFileAttributes네가 받을 수 있을거야

전화하는 것보다 훨씬 빠르다..length()의 결과로File.listFiles()또는 사용Files.size()의 결과로Files.newDirectoryStream()내 시험에서는 100배 정도 빨랐다.

사실, 나는 "ls"가 더 빠를지도 모른다고 생각해.Java에는 파일 정보 획득과 관련된 몇 가지 문제가 분명히 있다.불행히도 윈도우에 대해 동등한 재귀 ls의 안전한 방법은 없다.(cmd.exe의 DIR/S가 혼동되어 무한 루프에서 오류를 발생시킬 수 있음)

XP에서 LAN 상의 서버에 접속하면, Windows에서 폴더의 파일 수(33,000개)와 전체 크기를 얻는 데 5초가 걸린다.

이것을 자바에서 반복해서 반복하면 5분 이상 걸린다.나는 file.length(), file.lastModified(), file.toURI()를 하는 데 걸리는 시간을 측정하기 시작했고, 내가 발견한 것은 내 시간의 99%가 그 세 번의 통화에 의해 걸린다는 것이다.내가 해야 할 세 번의 전화는...

1000개의 파일의 차이는 15ms 로컬과 1800ms이다.Java의 서버 경로 검색은 터무니없이 느리다.네이티브 OS가 동일한 폴더를 빠르게 검색할 수 있다면 왜 Java를 검색할 수 없는가?

좀 더 완벽한 테스트로, 나는 XP의 WineMerge를 사용하여 수정된 날짜와 서버에 있는 파일의 크기를 로컬 파일들과 비교했다.이것은 각 폴더에 있는 33,000개의 파일의 전체 디렉토리 트리를 반복하고 있었다.총 시간, 7초 자바: 5분 이상.

그래서 OP의 원래 진술과 질문은 사실이고 타당하다.로컬 파일 시스템을 처리할 때 덜 눈에 띈다.33,000개의 아이템을 가진 폴더를 로컬로 비교하는 것은 WinMerge에서 3초가 걸리고, Java에서 로컬로 32초가 걸린다.자바 대 네이티브는 이러한 초보적인 테스트에서 10배 더 느리게 나타난다.

Java 1.6.0_22(최신), 기가비트 LAN 및 네트워크 연결, ping이 1ms 미만(둘 다 동일한 스위치에 있음)

자바는 느리다.

GHAD의 벤치마크에서 사람들이 언급한 몇 가지 이슈가 있다.

1>BalusC가 언급한 바와 같이: stream.available()은 이 경우 흐른다.

왜냐하면 사용 가능한()은 이 입력 스트림에 대한 메서드의 다음 호출에 의해 차단되지 않고 이 입력 스트림에서 읽거나 건너뛸 수 있는 바이트 수의 추정치를 반환하기 때문이다.

먼저 이 접근방식의 URL을 제거하십시오.

2>스튜어트H가 언급한 바와 같이 - 테스트 실행 순서도 캐시 차이를 만들므로 따로 테스트 실행으로 분리한다.


이제 테스트를 시작하십시오.

CHANNEL 1회 주행 시:

CHANNEL sum: 59691, per Iteration: 238.764

단독으로 1회 주행하는 길이:

LENGTH sum: 48268, per Iteration: 193.072

여기서 LENTERY 1이 우승자인 것 같다.

@Override
public long getResult() throws Exception {
    File me = new File(FileSizeBench.class.getResource(
            "FileSizeBench.class").getFile());
    return me.length();
}

참조URL: https://stackoverflow.com/questions/116574/java-get-file-size-efficiently

반응형