Разбить большой файл без копирования? - PullRequest
7 голосов
/ 07 октября 2009

Вопрос: Существуют ли вызовы Windows API (возможно, только NTFS), которые позволяют разбить очень большой файл на множество других без фактического копирования каких-либо данных (другими словами, указать логические точки останова между присоединенными файлами с именами и размерами файлов)?

Примеры: SetFileValidData, NtSetInformationFile

Сценарий: Мне нужно программно распространять / копировать 10 ГБ файлов с нелокального диска (включая сетевые, USB и DVD-диски). Он состоит из более чем 100 000 отдельных файлов со средним размером около 16 Кбайт, но объединен в блоки размером ~ 2 ГБ.

Однако использование простого API-интерфейса FileStream (буфер 64 КБ) для извлечения файлов из фрагментов на нелокальных дисках в отдельные файлы на локальном жестком диске на моей машине ограничено до 4 МБ / с, тогда как копирование всех фрагментов использование Explorer происходит со скоростью более 80 МБ / с!

Кажется логичным копировать целые куски, но дать Windows достаточно информации для логического разделения файлов (что теоретически должно произойти очень, очень быстро).

Разве установка Vista не делает что-то подобное?

Ответы [ 6 ]

3 голосов
/ 07 октября 2009

Вы не можете на практике. Данные должны физически перемещаться, если любая новая граница не совпадает с существующей границей кластера.

Для высокоскоростной копии прочитайте входной файл в асинхронном режиме, разбейте его на сегменты по 16 КБ, разместите их в очереди (в памяти) и настройте пул потоков для очистки очереди, записав эти сегменты по 16 КБ. Учитывая эти размеры, записи, вероятно, могут быть синхронными. Учитывая скорость локального ввода-вывода и удаленного ввода-вывода, а также тот факт, что у вас есть несколько потоков записи, вероятность переполнения вашей очереди должна быть довольно низкой.

3 голосов
/ 07 октября 2009

Несмотря на то, что есть теневые копии томов, это подход "все или ничего" - вы не можете вырезать только часть файла. Они также являются только временными. Аналогично, жесткие ссылки делятся всем контентом без исключения. К сожалению, вырезание только частей файла не поддерживается в Windows, хотя некоторые экспериментальные файловые системы Linux, такие как btrfs, поддерживают это.

0 голосов
/ 10 ноября 2009

Вы можете скопировать второй кусок файла в новый файл и затем обрезать исходный файл. При таком подходе вы копируете только половину файла.

0 голосов
/ 19 октября 2009

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

from __future__ import division
import os
import sys
from win32file import CreateFile, SetEndOfFile, GetFileSize, SetFilePointer, ReadFile, WriteFile
import win32con
from itertools import tee, izip, imap

def xfrange(start, stop=None, step=None):
    """
    Like xrange(), but returns list of floats instead

    All numbers are generated on-demand using generators
    """

    if stop is None:
        stop = float(start)
        start = 0.0

    if step is None:
        step = 1.0

    cur = float(start)

    while cur < stop:
        yield cur
        cur += step


# from Python 2.6 docs
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

def get_one_hundred_pieces(size):
    """
    Return start and stop extents for a file of given size
    that will break the file into 100 pieces of approximately
    the same length.

    >>> res = list(get_one_hundred_pieces(205))
    >>> len(res)
    100
    >>> res[:3]
    [(0, 2), (2, 4), (4, 6)]
    >>> res[-3:]
    [(199, 201), (201, 203), (203, 205)]
    """
    step = size / 100
    cap = lambda pos: min(pos, size)
    approx_partitions = xfrange(0, size+step, step)
    int_partitions = imap(lambda n: int(round(n)), approx_partitions)
    partitions = imap(cap, int_partitions)
    return pairwise(partitions)

def save_file_bytes(handle, length, filename):
    hr, data = ReadFile(handle, length)
    assert len(data) == length, "%s != %s" % (len(data), length)
    h_dest = CreateFile(
        filename,
        win32con.GENERIC_WRITE,
        0,
        None,
        win32con.CREATE_NEW,
        0,
        None,
        )
    code, wbytes = WriteFile(h_dest, data)
    assert code == 0
    assert wbytes == len(data), '%s != %s' % (wbytes, len(data))

def handle_command_line():
    filename = sys.argv[1]
    h = CreateFile(
        filename,
        win32con.GENERIC_WRITE | win32con.GENERIC_READ,
        0,
        None,
        win32con.OPEN_EXISTING,
        0,
        None,
        )
    size = GetFileSize(h)
    extents = get_one_hundred_pieces(size)
    for start, end in reversed(tuple(extents)):
        length = end - start
        last = end - 1
        SetFilePointer(h, start, win32con.FILE_BEGIN)
        target_filename = '%s-%d' % (filename, start)
        save_file_bytes(h, length, target_filename)
        SetFilePointer(h, start, win32con.FILE_BEGIN)
        SetEndOfFile(h)

if __name__ == '__main__':
    handle_command_line()

Это скрипт Python 2.6, использующий pywin32 для использования API-интерфейсов Windows. Та же самая техника может быть легко реализована в Delphi или C ++.

Основная процедура находится в handle_command_line. Он берет имя файла и разбивает его на части в зависимости от функции get_one_hundred_pieces. Ваше приложение заменит более подходящую функцию для определения соответствующих экстентов.

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

Я проверил это в отношении файла размером 1 ГБ, разбитого на 100 частей, и он запустился менее чем за 30 секунд. Кроме того, теоретически это должно выполняться с эффективным использованием пространства (не потребляя больше, чем общий размер файла плюс наибольший размер чанка в любой момент времени). Я подозреваю, что есть улучшения производительности, но это в основном подтверждение концепции.

0 голосов
/ 08 октября 2009

Мысль по этому поводу: достаточно ли места для копирования большого чанка на локальный диск и последующей работы на нем, используя его как файл с отображением в памяти? Я помню где-то обсуждение, когда эти файлы работают намного быстрее, так как они используют кэш Windows / File / Page и легко настраиваются.

Из Википедии и из StackOverflow

0 голосов
/ 07 октября 2009

Есть ли причина, по которой вы не можете вызвать процедуры копирования ОС для копирования? Это должно сделать то же самое, что и Explorer. Это сводит на нет необходимость вашего странного разделения, которое, я думаю, не существует.

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