Началось все с того, что приобрел я MP3-проигрыватель с 1GB флэш-памяти и интерфейсом USB2.0. Испробовав его на практике, я был несколько разочарован: теоретически скорость записи данных на него должна была быть 3-4 МБ в секунду, на самом деле она почему-то не превышала 0,5-1 МБ/сек. “Ничего страшного”, — подумал я. Файлы на плеер я заливал нечасто, поэтому решил оставить все так как было. Но однажды я натолкнулся в сети на программу Flash Memory Toolkit. Она показывает скорость чтения/записи на флэш-память в зависимости от размера файлов, на нее записываемых. Скачав ее и установив, проверил свой плеер и был поражен: скорость записи для больших файлов была на уровне 4МБ/секунду. Значит инструкция для плеера таки не обманывала: записывать файлы на такой скорости можно! Тем временем истек срок бесплатного использования Flash Memory Toolkit, искать взломанную версию не хотелось, и я решил написать похожую программу сам, тем более было интересно, в чем же тут дело. Писал я на Delphi 5-й версии, соответственно и весь код здесь на нем же.

Первым делом нужно было написать процедуру, которая создавала бы файл на диске и записывала в него заданное количество байт каких-нибудь данных. С этим я справился быстро, код получился примерно такой:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const
    bufsize:integer=1024;
    TestFile='g:test.tst';
var
    buf:array[1..bufsize] of byte;

procedure WFile(Size{Kilobytes}:integer);
var
    f:TFileStream;
    bufs,j:integer;
begin
    f:=TFileStream.Create(TestFile,fmOpenWrite);
    try
        bufs:=size*1024 div bufsize;
        for j:=1 to bufs do
            f.Write(buf,bufsize);
    finally
        f.free;
    end;
end;
Сначала определяем буфер buf размером bufsize байт, потом открываем файл с именем TestFilePath на запись, определяем количество буферов, которые нужно записать на диск и производим запись. Осталось только подсчитать время которое тратит компьютер на выполнение этой процедуры, например так:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
procedure TestWrite;
var
    t1,t2:Cardinal;
    cnt:integer;
begin
    t1:=GetTickCount;
    cnt:=0;
    repeat
        WFile(512);
        t2:=GetTickCount;
        inc(cnt);
    until (t2-t1)>10000;
    ShowMessage(IntToStr(Trunc(512.0*cnt*1000.0/(t2-t1)));
end;
Функция GetTickCount возвращает нам количество миллисекунд, которое прошло со времени загрузки Windows. Сначала запомним его в переменной t1, потом организуем цикл, в котором будем несколько раз вызывать процедуру записи нашего тестового файла (размером 512КБ) на диск. Из цикла выходим только по истечении 10 секунд времени (10000 мс).

Ну и в конце вычислим скорость записи и покажем ее на экране. Проверка скорости чтения производится аналогично, только нужно в процедуре WFile поменять константу fmOpenWrite на fmOpenRead. а вызов f.write на f.read.

Костяк программы был готов, запустив ее я получил: скорость записи: 60КБ/сек, чтения: 61000 КБ/сек!!! Что-то здесь было не так! Со скоростью чтения все оказалось понятно: просто операционная система не читала фактически наш файл с диска, а брала его из кэша. Но что со скоростью записи? Отложив в сторону Delphi, я вылез в интернет и стал искать способ отключить кэширование чтения файла на диске и скоро нашел ответ, который находился в параметрах функции CreateFile WinAPI. Чтобы открыть файл для чтения или записи минуя кэш, нужно передать этой функции параметр FILE_FLAG_NO_BUFFERING.

Кроме того, CreateFile возвратит нам дескриптор открытого файла, который нужно использовать при создании потока THandleStream. Также следует это дескриптор потом закрыть при помощи функции CloseHandle. Теперь моя процедура записи выглядела так:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
procedure WFile(Size{Kilobytes}:integer);
var
    f:TFileStream;
    bufs,j:integer;
begin
    h:=CreateFile(PChar(TestFile),GENERIC_WRITE,0,nil,CREATE_ALWAYS,FILE_FLAG_NO_BUFFERING,0);
    if h=INVALID_HANDLE_VALUE then exit;
    f:=THandleStream.Create(h);
    try
        bufs:=size*1024 div bufsize;
        for j:=1 to bufs do
            f.Write(buf,bufsize);
    finally
        f.free;
        CloseHandle(h);
    end;
end;
Запускаем: скорость записи: 60КБ/сек, чтения: 7300 КБ/сек! Ну вот, с чтением все в порядке, а что запись? И тут я подумал, а не увеличить ли мне размер буфера buf? Попробую:
1
const bufsize:integer=512*1024;
Задаем размер буфера 512КБ, запускаем, и вот результат: скорость записи: 1308КБ/сек, чтения: 7300 КБ/сек! Вот в чем дело, оказывается размер буфера для копирования имеет огромное значение!

Так что же происходит? Дело в том, что кроме собственно данных, на диск (или флэш-накопитель) нужно записывать разную служебную информацию: если у вас файловая система FAT, то как минимум нужно записывать данные о файле в таблицу размещения файлов и в каталог, в котором этот файл находится. Причем, на жестких дисках таблица размещения файлов кэшируется в памяти, а на съемных дисках – нет (кэширование записи на съемных дисках по умолчанию отключено в Windows XP, это связано с риском повреждения структуры FAT, если во время записи файлов на флэшку попробовать выдернуть ее из разъема USB). Не знаю, в какой степени это относится к NTFS, обычно все флэш-накопители форматируются в FAT или FAT32. То есть, судя по такой низкой скорости записи при небольшом размере буфера, информация о файле в FAT и каталоге физически записываются при каждом вызове f.write, которая обращается к системному вызову Windows. Поэтому, конечно, делать это нужно по возможности реже, то есть увеличить размер буфера.

Во-вторых, в флэш-памяти существует ограничение на максимальное количество циклов записи, опять же в случае с небольшим буфером, одна и та же информация о файле в каталоге (в частности размер его) будет перезаписываться очень много раз, что снижает надежность нашего накопителя (да еще и в системной области)! И в третьих, по той же самой причине, флэш-накопители показывают более высокую скорость записи на больших файлах. Ну вот и все, теперь мне осталось прикрутить к программе всякие красивости вроде графиков, выбора диска и объема буфера для тестирования. Что получилось, посмотрите на рисунке 1. 1 Это график тестирования моего плеера при размере буфера в 16 килобайт, скорость записи очень низкая. На рисунке 2 тот же накопитель, но размер буфера уже 512 килобайт – значительно увеличилась скорость записи, скорость чтения возросла на треть. 2 Ну и на рисунке 3 размер буфера – 8 мегабайт, взгляните на скорость записи очень больших файлов – 4,4 МБ/сек! 3 Интересно посмотреть на график тестирования обычного винчестера (рисунки 4 и 5), размер буфера тут сильного влияния не оказывает из-за кэширования записи операционной системой и самим винчестером (кстати из графиков видно, что размер буфера моего жесткого диска – 2 мегабайта). 4 5 Теперь мне было понятно, почему копирование на мой плеер проходило так долго: очевидно в файл-менеджерах, которыми я пользуюсь, размер буфера в программе невелик, и запись происходит очень долго. Но можно ли вообще увеличить этот буфер? Обычно я пользуюсь программой WinNavigator 1.95, но даже при включенной опции «Использовать системную функцию копирования» никаких изменений в скорости копирования не происходит. В “Проводнике” Windows ничего делать не нужно (да и не получится), он копирует на неплохой скорости, TotalCommander поддается настройке как показано на рисунке 6. 6 В FARManager поставьте галочку в “Настройки – Системные — Использовать системную функцию копирования”.

Выводы

Итак, если хотите быстро записывать на флэшки:
  1. Настройте свой файл-менеджер
  2. Не копируйте много маленьких файлов, лучше заархивируйте их в один большой архив и скопируйте его
  3. Можете попробовать включить кэширование записи на съемных носителях, но это несколько рискованно
И напоследок, мою программу с исходными текстами можно скачать по кнопке ниже. Удачи!
Filebench
Filebench
filebench01.zip
Version: 0.1
270.7 KiB
98 Downloads
Детали