Как написать сверхбыстрый передающий потоком файл код в C#?

Я обнаружил, что с помощью uWSGI Decorators довольно проще, чем с помощью Celery, если вам нужно просто выполнить длинную задачу в фоновом режиме. Думайте, что Celery - лучшее решение для серьезного тяжелого проекта, и это требует дополнительных усилий.

Для начала использования uWSGI Decorators вам просто нужно обновить конфигурацию uWSGI с помощью

1
/here/the/path/to/dir

написать код, например:

@spoolraw
def long_task(arguments):
    try:
        doing something with arguments['myarg'])
    except Exception as e:
        ...something...
    return uwsgi.SPOOL_OK

def myView(request)
    long_task.spool({'myarg': str(someVar)})
    return render_to_response('done.html')

, чем при запуске просмотра в журнале uWSGI появляется:

[spooler] written 208 bytes to file /here/the/path/to/dir/uwsgi_spoolfile_on_hostname_31139_2_0_1359694428_441414

, а когда задача выполнена:

[spooler /here/the/path/to/dir pid: 31138] done with task uwsgi_spoolfile_on_hostname_31139_2_0_1359694428_441414 after 78 seconds

Существуют странные (для меня) ограничения:

    - spool can receive as argument only dictionary of strings, look like because it's serialize in file as strings.
    - spool should be created on start up so "spooled" code it should be contained in separate file which should be defined in uWSGI config as pyFileWithSpooledCode

39
задан Peter Mortensen 28 June 2015 в 22:56
поделиться

8 ответов

Я не верю, что в .NET есть что-либо, позволяющее копировать раздел файла без буферизации его в памяти . Однако мне кажется, что это в любом случае неэффективно, так как нужно открывать входной файл и искать много раз. Если вы просто разбиваете файл, почему бы не открыть входной файл один раз, а затем просто написать что-то вроде:

public static void CopySection(Stream input, string targetFile, int length)
{
    byte[] buffer = new byte[8192];

    using (Stream output = File.OpenWrite(targetFile))
    {
        int bytesRead = 1;
        // This will finish silently if we couldn't read "length" bytes.
        // An alternative would be to throw an exception
        while (length > 0 && bytesRead > 0)
        {
            bytesRead = input.Read(buffer, 0, Math.Min(length, buffer.Length));
            output.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
    }
}

Это неэффективно при создании буфера при каждом вызове - вы можете захотеть чтобы создать буфер один раз и передать его в метод:

public static void CopySection(Stream input, string targetFile,
                               int length, byte[] buffer)
{
    using (Stream output = File.OpenWrite(targetFile))
    {
        int bytesRead = 1;
        // This will finish silently if we couldn't read "length" bytes.
        // An alternative would be to throw an exception
        while (length > 0 && bytesRead > 0)
        {
            bytesRead = input.Read(buffer, 0, Math.Min(length, buffer.Length));
            output.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
    }
}

Обратите внимание, что при этом также закрывается выходной поток (из-за оператора using), чего не было в исходном коде.

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

Я думаю , это будет значительно быстрее, но, очевидно, вам нужно попробовать его, чтобы увидеть ...

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

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

Я думаю это будет значительно быстрее, но, очевидно, вам нужно попробовать, чтобы увидеть ...

Это, конечно, предполагает наличие непрерывных фрагментов. Если вам нужно пропустить биты файла, вы можете сделать это вне метода. Кроме того, если вы пишете очень маленькие файлы, вы можете также захотеть выполнить оптимизацию для этой ситуации - самый простой способ сделать это, вероятно, - ввести BufferedStream , обертывающий входной поток.

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

Я думаю это будет значительно быстрее, но, очевидно, вам нужно попробовать, чтобы увидеть ...

Это, конечно, предполагает наличие непрерывных фрагментов. Если вам нужно пропустить биты файла, вы можете сделать это вне метода. Кроме того, если вы пишете очень маленькие файлы, вы можете также захотеть выполнить оптимизацию для этой ситуации - самый простой способ сделать это, вероятно, - ввести BufferedStream , обертывающий входной поток.

Я думаю , что это будет значительно быстрее, но, очевидно, вам нужно попробовать, чтобы увидеть ...

Это, конечно, предполагает наличие смежных блоков. Если вам нужно пропустить биты файла, вы можете сделать это вне метода. Кроме того, если вы пишете очень маленькие файлы, вы можете также захотеть выполнить оптимизацию для этой ситуации - самый простой способ сделать это, вероятно, - ввести BufferedStream , обертывающий входной поток.

Я думаю , что это будет значительно быстрее, но, очевидно, вам нужно попробовать, чтобы увидеть ...

Это, конечно, предполагает наличие смежных блоков. Если вам нужно пропустить биты файла, вы можете сделать это вне метода. Кроме того, если вы пишете очень маленькие файлы, вы можете также захотеть выполнить оптимизацию для этой ситуации - самый простой способ сделать это, вероятно, - ввести BufferedStream , обертывающий входной поток.

46
ответ дан 27 November 2019 в 02:24
поделиться

Насколько велика длина ? Возможно, вам лучше повторно использовать буфер фиксированного размера (умеренно большой, но не непристойный) и забыть о BinaryReader ... просто используйте Stream.Read и Stream. Напишите .

(отредактируйте) что-то вроде:

private static void copy(string srcFile, string dstFile, int offset,
     int length, byte[] buffer)
{
    using(Stream inStream = File.OpenRead(srcFile))
    using (Stream outStream = File.OpenWrite(dstFile))
    {
        inStream.Seek(offset, SeekOrigin.Begin);
        int bufferLength = buffer.Length, bytesRead;
        while (length > bufferLength &&
            (bytesRead = inStream.Read(buffer, 0, bufferLength)) > 0)
        {
            outStream.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
        while (length > 0 &&
            (bytesRead = inStream.Read(buffer, 0, length)) > 0)
        {
            outStream.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
    }        
}
6
ответ дан 27 November 2019 в 02:24
поделиться

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

Если длина не слишком велика, вы также можете попытаться сгруппировать несколько вызовов копирования, сгруппировав смещения, которые близки друг к другу и чтение всего блока, который вам нужен, например:

offset = 1234, length = 34
offset = 1300, length = 40
offset = 1350, length = 1000

можно сгруппировать в одно чтение:

offset = 1234, length = 1074

Тогда вам нужно только «искать» в своем буфере и записать оттуда три новых файла без придется читать снова.

3
ответ дан 27 November 2019 в 02:24
поделиться

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

static void Main(string[] args)
    {
        Dispatcher dp = new Dispatcher();
        DispatcherQueue dq = new DispatcherQueue("DQ", dp);

        Port<long> offsetPort = new Port<long>();

        Arbiter.Activate(dq, Arbiter.Receive<long>(true, offsetPort,
            new Handler<long>(Split)));

        FileStream fs = File.Open(file_path, FileMode.Open);
        long size = fs.Length;
        fs.Dispose();

        for (long i = 0; i < size; i += split_size)
        {
            offsetPort.Post(i);
        }
    }

    private static void Split(long offset)
    {
        FileStream reader = new FileStream(file_path, FileMode.Open, 
            FileAccess.Read);
        reader.Seek(offset, SeekOrigin.Begin);
        long toRead = 0;
        if (offset + split_size <= reader.Length)
            toRead = split_size;
        else
            toRead = reader.Length - offset;

        byte[] buff = new byte[toRead];
        reader.Read(buff, 0, (int)toRead);
        reader.Dispose();
        File.WriteAllBytes("c:\\out" + offset + ".txt", buff);
    }

Этот код отправляет смещения в порт CCR, что вызывает создание потока для выполнения кода в методе Split. Это заставляет вас открывать файл несколько раз, но избавляет от необходимости синхронизации. Вы можете сделать память более эффективной, но вам придется пожертвовать скоростью.

3
ответ дан 27 November 2019 в 02:24
поделиться

Первое, что я бы порекомендовал, - это провести измерения. Где ты теряешь время? Это при чтении или записи?

Более 100 000 обращений (суммируйте время): Сколько времени уходит на выделение буферного массива? Сколько времени уходит на открытие файла для чтения (каждый раз это один и тот же файл?) Сколько времени тратится на операции чтения и записи?

Если вы не выполняете никаких преобразований в файле, нужен ли вам BinaryWriter или вы можете использовать файловый поток для записи? (попробуйте, вы получите идентичный результат? Это сэкономит время?)

1
ответ дан 27 November 2019 в 02:24
поделиться

Использование FileStream + StreamWriter Я знаю, что можно создавать большие файлы за короткое время (менее 1 мин 30 секунд). Я генерирую три файла общим объемом более 700 мегабайт из одного файла, используя эту технику.

Ваша основная проблема с кодом, который вы используете, заключается в том, что вы каждый раз открываете файл. Это создает накладные расходы на ввод-вывод файлов.

Если бы вы знали имена файлов, которые вы будете генерировать заранее, вы могли бы извлечь File.OpenWrite в отдельный метод; это увеличит скорость. Не видя кода, который определяет, как вы разделяете файлы, я не думаю, что вы можете стать намного быстрее.

1
ответ дан 27 November 2019 в 02:24
поделиться

Никто не предлагает использовать потоки? Написание файлов меньшего размера похоже на пример из учебника, где можно использовать потоки. Настройте несколько потоков для создания файлов меньшего размера. Таким образом, вы можете создавать их все параллельно, и вам не нужно ждать завершения каждого из них. Я предполагаю, что создание файлов (операция с диском) займет намного больше времени, чем разделение данных. и, конечно, сначала вы должны убедиться, что последовательного подхода недостаточно.

0
ответ дан 27 November 2019 в 02:24
поделиться

(Для справки в будущем.)

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

Файлы с отображением памяти поддерживаются в управляемом коде в .NET 4.0.

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

-1
ответ дан 27 November 2019 в 02:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: