1. Пользуйтесь тегами кода. - [code] ... [/code] 2. Точно указывайте язык, название и версию компилятора (интерпретатора). 3. Название темы должно быть информативным. В описании темы указываем язык!!!
Мне очень не нравится скорость побайтового чтения - 3МБ файл читается больше секунды, когда дельфовый аналог на BlockRead считывает его моментально. Всё, что приходит в голову - открыть файл для типа byte, узнать размер, потом открыть файл для массива данного размера и считать за раз.
Byte_IO.Open(SF, Byte_IO.In_File, "test.txt"); declare S: Byte_IO.Count := Byte_IO.Size(SF); type Arr is array (1..S) of byte; type AArr is access Arr; package Arr_IO is new Ada.Direct_IO(Arr); F: Arr_IO.File_Type; A: AArr; begin A := new Arr; Byte_IO.Close(SF); Arr_IO.Open(F, Arr_IO.In_File, "test.txt"); Arr_IO.Read(F, A.all); Arr_IO.Close(F); end;
Но это выдаёт raised STORAGE_ERROR : EXCEPTION_STACK_OVERFLOW (даже после того, как я вместо заведения массива на стеке завёл указатель) Да и мало ли размер файла изменится между первым и вторым открытиями. И вообще много дёргать винт это плохо.
Последний вариант - через ВинАПИ, но мне неохота туда лезть.
ПС Настраиваемые пакеты во внутреннем блоке - это сильно. Реально круто, я про язык.
Но это выдаёт raised STORAGE_ERROR : EXCEPTION_STACK_OVERFLOW
Не подтверждается... В пустом проекте читает трехмегабайтный файл легко...
Цитата
Всё, что приходит в голову - открыть файл для типа byte, узнать размер, потом открыть файл для массива данного размера и считать за раз.
Чтобы узнать размер файла, совсем не обязательно его открывать. Есть пакет Directories, который предоставляет тебе информацию:
procedure File_Read is Name : String := "F:\Programs\Ada\forum\debug\test.txt"; File_Size : Integer := Integer (Ada.Directories.Size (Name));
type Byte_Buffer is array(1 .. File_Size) of Byte; package Arr_IO is new Ada.Direct_IO (Byte_Buffer); File : Arr_IO.File_Type; Buffer : Byte_Buffer; begin Arr_IO.Open (File, Arr_IO.In_File, Name); Arr_IO.Read (File, Buffer); Arr_IO.Close (File); Ada.Text_IO.Put_Line (Integer'Image (File_Size)); end File_Read;
шестимегабайтный файл прочитало меньше, чем за 0.1 секунды.
Так, я понял. При инициализации шаблона там, в модуле файлов, на стеке создаётся массив соответствующего размера.
> Не подтверждается... В пустом проекте читает трехмегабайтный файл легко...
А где в настройках компилятора размер стека правится? Впрочем, это не решение. А если я захочу в кучу прочитать полугиговый файл за раз? Ну допустим, что это неправильно: я могу с той же скоростью читать порциями по 4096 байт, но это же привязывание к размеру кластера (т.е. к особенностям машины). Жаль, что стандартный файловый тип не умеет буферизовать (как я понял).
> Чтобы узнать размер файла, совсем не обязательно его открывать. Есть пакет Directories, который предоставляет тебе информацию:
Спасибо. Всё-таки, каков оптимальный размер для считывания за раз? Может, есть стандартная функция, которая это говорит, по аналогии с функцией, говорящей оптимальный размер блока для выделения памяти в куче?
Жаль, что стандартный файловый тип не умеет буферизовать
Работай с потоками, они умеют. Если правильно организовать ввод из файла, как из потока (правильно - это чтоб не было лишних копирований буфера с места на место, чтоб читать прямо туда, где физически буфер расположен в памяти), то работать будет очень быстро. Года полтора назад на comp.lang.ada некто Gautier (есть такой программист, широко известный в узких кругах) показывал разницу в скорости обработки потока: читал и перезаписывал содержимое одного файла в другой. Первый раз - обычными 'Read/'Write, второй раз - правильно работая с буфером. Так вот второй способ отработал чуть-ли не в 50 раз быстрее (подробностей не помню, но если надо - могу поискать ту ветку).
Цитата
Может, есть стандартная функция, которая это говорит, по аналогии с функцией, говорящей оптимальный размер блока для выделения памяти в куче?
Да только я не умею... Из описания в книге я так и не понял, как должен выглядеть полный пример использования потока. Я не понял, как и что необходимо закрывать. Ну завёл я F: File_Type, ну открыл файл, ну написал S := Stream(F); В конце что делать с этими S и F? Если бы я работал только с F, то я бы просто написал Close, а тут что надо? С этим S надо что-то делать?
И ещё. Я не люблю вручную писать закрытие файла по исключению, и просто писать закрытие в конце тоже не люблю, я вообще считаю, что все подобные вещи должны быть завёрнуты в контролируемый объект (и тут простой РАИИ позволяет делать то, где навороченные, но недетерминированные ГЦ не помогут никак), вплоть до жёсткого контроля на уровне запрета компилятора.
Так вот, нету ли стандартного контролируемого потока?
begin -- Открыл Ada.Streams.Stream_IO.Open(SFIn, Ada.Streams.Stream_IO.In_File, File_Name); -- Получил размер Buf_Size := Integer (Ada.Streams.Stream_IO.Size (SFIn)); -- Выделил память Buffer := new Byte_Buffer (1 .. Buf_Size);
-- Проверил, все ли нормально if Buf_Size /= Bytes_Read then Ada.Text_IO.Put_Line ("Something goes wrong ..."); end if; -- Закрыл Ada.Streams.Stream_IO.Close(SFIn); end Main;
Не дожидаясь вопроса, сразу объясню, что такое Is_Workaround_Possible. У компиляторов GNAT и ObjectAda есть некоторые проблемы со скоростью выполнения Read и Write в потоках. Так вот, если Stream_Element это и есть байт, и оба тестовых массива одинаково упакованы и одинаково выровнены - то можно значительно ускорить операции чтения/записи, работая напрямую с массивом Stream_Element-ов, наложенным на буфер (строка for SE_Buffer'Address use Buffer'Address этим занимается). Если же нет возможности работать с таким массивом - что ж поделаешь - приходится читать медленно в буфер, это действительно медленно: автор замерял, говорит о 50-кратном замедлении в среднем. Если надо, кстати, есть и BlockWrite для стримов...
А почему именно от 1 до 19? Специально, чтобы не делилось на 4?
Дальше, умеет ли компилятор определять значение Is_Workaround_Possible, убрав одну из ветвей
if Is_Workaround_Possible then ... else ... end if;
> Ни файлов, ни потоков контролируемых нет и не будет.
То есть в стандартной библиотеке не будет? Себе я сделал так:
with Ada.Streams.Stream_IO; with Ada.Finalization;
package Controlled_Streams is
type Controlled_Stream_IO is new Ada.Finalization.Limited_Controlled with record F: Ada.Streams.Stream_IO.File_Type; S: Ada.Streams.Stream_IO.Stream_Access; end record;
function New_Stream_IO(Mode : Ada.Streams.Stream_IO.File_Mode; Name : String; Form : String := "") return Controlled_Stream_IO;
private
pragma Warnings(Off);
overriding procedure Initialize (CS_IO: in out Controlled_Stream_IO); overriding procedure Finalize (CS_IO: in out Controlled_Stream_IO);
pragma Warnings(On);
end Controlled_Streams;
package body Controlled_Streams is
overriding procedure Initialize (CS_IO: in out Controlled_Stream_IO) is begin null; end;
overriding procedure Finalize (CS_IO: in out Controlled_Stream_IO) is begin if Ada.Streams.Stream_IO.Is_Open(CS_IO.F) then Ada.Streams.Stream_IO.Close(CS_IO.F); end if; end;
function New_Stream_IO(Mode : Ada.Streams.Stream_IO.File_Mode; Name : String; Form : String := "") return Controlled_Stream_IO is begin return CS_IO: Controlled_Stream_IO do CS_IO.S := null; Ada.Streams.Stream_IO.Open(CS_IO.F, Mode, Name, Form); CS_IO.S := Ada.Streams.Stream_IO.Stream(CS_IO.F); end return; end;
end Controlled_Streams;
Использование:
declare CS: Controlled_Stream_IO := New_Stream_IO(In_File, "test.txt"); begin тут работаем, про исключения и необходимость закрыть не думаем end;
Добавлено через 10 мин. Ещё такой момент. Я применил это для своего вектора. Всё зашибись, но возник облом на файле 3МБ (какое-то исключение raised ADA.IO_EXCEPTIONS.DEVICE_ERROR : Invalid argument), причём именно в случае Is_Workaround_Possible. Я принудительно выставил Is_Workaround_Possible в false. И всё нормально прочиталось, при этом программа отработала за 0.21 секунд (я так понял, тут уже не время считывания, а время запуска, инициализаций итд).
возник облом на файле 3МБ (какое-то исключение raised ADA.IO_EXCEPTIONS.DEVICE_ERROR : Invalid argument), причём именно в случае Is_Workaround_Possible. Я принудительно выставил Is_Workaround_Possible в false. И всё нормально прочиталось
Очень странно. Воообще-то такое поведение должно было иметь место в обратном случае: если Is_Workaround_Possible равно False, а ты насильно перекинул в True, и из-за несоответствия размеров и(ли) выравнивания процедура Read не смогла прочесть информацию в SE_Buffer. В таком случае Read бросает IO_Exceptions.Device_Error... Получается, что Is_Workaround_Possible вернула True, но буферные переменные не соответствуют друг другу?
Попробуй действовать наверняка: прямо внутри BlockRead опиши подтипы:
subtype mysz_Test_A is Byte_Buffer(1 .. Buffer'Length); subtype mysz_Test_B is Ada.Streams.Stream_Element_Array(1 .. buffer'Length);
, и проверяй размер/выравнивание этих подтипов. У меня при подключении твоего пакета без сбоя прочитались 3-х, 6-ти и 9-ти Мб файлы. И не только прочитались, а и скопировались в другой стрим (ну да, я добавил еще создание файла в твой код). Хотя, если тебя скорость устраивает... Может, в 2010 уже починили эту багу просто? в GNAT-2009 с этим невозможно было мириться.
begin V.Set_Length(0); V.Set_Length(El_Count); if Is_Workaround_Possible then declare Buffer : Test_SA; for Buffer'Address use V.Elements.all'Address; Last : Stream_Element_Offset; pragma Unreferenced(Last); begin Read(File.S.all, Buffer, Last); end; else Element_Array'Read(File.S, V.Elements.all (V.First .. V.Last)); end if; end;
Вылетает.
Если же Is_Workaround_Possible сделать в лоб false, то всё нормально. ________________________________ Правка: Всё, я заменил Element'Size на Element'Size/8 и всё заработало не только на мелких файлах. Ну и формулу integer(Size(File.F)) * 8 / Element'Size; поменял на integer(Size(File.F)) / (Element'Size / 8); (чтобы на больших файлах не было лишнего переполнения)
Всё-таки иногда забываю, что размер возвращается в битах, а не байтах.