CW10: обработка бинарных файлов

Clarion, Clarion 7

Модератор: Дед Пахом

Правила форума
При написании вопроса или обсуждении проблемы, не забывайте указывать версию Clarion который Вы используете.
А так же пользуйтесь спец. тегами при вставке исходников!!!
Ответить
SuperMax
Посетитель
Сообщения: 47
Зарегистрирован: 25 Август 2015, 3:22
Поблагодарили: 2 раза

CW10: обработка бинарных файлов

Сообщение SuperMax »

Задачка по сути простая - есть бинарные файлы объема 300-700 Мегабайт
надо быстро их считать в память и быстро записать новые

сейчас код такой

считывание

Код: Выделить всё

InFile  FILE,DRIVER('DOS','/FILEBUFFERS=65535 /QUICKSCAN=ON '),NAME(file_in),PRE(FIN),BINDABLE
          RECORD,PRE()                                 !Input file
Buffer      BYTE(1),DIM(65535)
          END
        END



progress       REAL(0)
window WINDOW('Обработка файла'),AT(,,340,113),FONT('MS Sans Serif',8,,FONT:bold),CENTER, |
         GRAY,DOUBLE,AUTO,MODAL
       PROGRESS,USE(progress),AT(37,49,263,15),RANGE(0,100)
     END

 CODE


        if mem>0            ! если память уже выделена, то освобождаем
            free(mem)
        END
        mem = malloc(msize)  ! выделение памяти        
               
        IF mem = ''                         
            MESSAGE('Insufficient memory available','ERROR',ICON:Hand)                
            RETURN                                    
        END        
                      
        progress=1        
        open(window)       
        DISPLAY()
        
               
        OPEN(INFile,0)      ! откроем файл
        IF ERRORCODE()      ! обработка ошибок
            MESSAGE('Ошибка ' & ERRORCODE() & ' при открытии файла: '& file_in & ERROR(),'ERROR',ICON:Hand)
            RETURN                                    
        END

        length_file = bytes(infile)         !длина файла
        filesize1=0                                              
        DISPLAY()
        !Рассмотрим варианты от размера файла
        if length_file<65535 then !Файл меньше  64к
            get(INFILE,1,length_file)
            IF ERRORCODE() THEN
                MESSAGE('Ошибка ' & ERRORCODE() & ' при чтении файла: '& file_in & ERROR(),'ERROR',ICON:Hand)
                RETURN
            END
            idx=1
            loop length_file times
                buff=FIN:buffer[idx]            
                memcpy(mem+idx, ADDRESS(buff), 1)   !  void *memcpy( void *dest, const void *src, size_t count );                        
                idx+=1
                progress+=100/length_file
                DISPLAY()
            END
        else 
            loop while (length_file-filesize1)>65535 !файл больше 64к
                get(infile,(filesize1+1),65535)
                IF ERRORCODE() THEN
                    MESSAGE('Ошибка ' & ERRORCODE() & ' при чтении файла: '& file_in & ERROR(),'ERROR',ICON:Hand)
                    RETURN
                END
                idx=1
                loop 65535 times            
                    buff=FIN:buffer[idx]
                    memcpy(mem+filesize1, ADDRESS(buff), 1)
                    idx+=1; filesize1+=1                                
                END        
                progress+=100/(length_file/65535)
                DISPLAY()          
            END
            get(infile,(filesize1+1),(length_file-filesize1))
            idx=1
            loop length_file-filesize1 times            
                buff=FIN:buffer[idx]                
                memcpy(mem+filesize1, ADDRESS(buff), 1)                
                idx+=1; filesize1+=1                
            END
            progress=100
            DISPLAY()
        END
        close(infile)        
        close(window)

запись вот такая

Код: Выделить всё

OutFile  FILE,DRIVER('DOS','/FILEBUFFERS=65535 /QUICKSCAN=ON '),NAME(file_out),PRE(FOUT),BINDABLE,CREATE
          RECORD                                 !Input file
Buffer      BYTE(1)!,DIM(65535)
          END
        END

progress       REAL(0)
window WINDOW('Обработка файла'),AT(,,340,113),FONT('MS Sans Serif',8,,FONT:bold),CENTER, |
         GRAY,DOUBLE,AUTO,MODAL
       PROGRESS,USE(progress),AT(37,49,263,15),RANGE(0,100)
     END

 CODE

      
    progress=1        
    open(window)       
    DISPLAY()
        
  CREATE(OutFile)                                   !Open the file                      
  OPEN(OutFile,2)                                   !Open the file
  IF ERRORCODE()                                 ! aborting on any error
    MESSAGE('Ошибка ' & ERRORCODE() & ' при открытии файла: '& file_in & ERROR(),'ERROR',ICON:Hand)
    RETURN                                    
  END
   
   DISPLAY()
    
    LOOP W# = START_POZ_WRFILE TO length_file
            memcpy(ADDRESS(buff), mem+W#, 1)
            FOUT:buffer= buff      
            append(outfile)
            IF ERRORCODE()                                 ! aborting on any error
                MESSAGE('Ошибка ' & ERRORCODE() & ' при записи в файл: '& file_out & ERROR(),'ERROR',ICON:Hand)
                RETURN                                    
            END
    END
    progress=100
    DISPLAY()
    close(OUTfile)        
    close(window)

Задача в принципе решена, однако скорость загрузки и записи несколько огорчает - те уходит несколько минут
(тк в записи вырезано все связанное с отображением прогресс-бара то она идет быстрее)
те 600мег пишется примерно за 6 минут

Вопрос - всё ли сделано для получения максимального быстродействия или я что-то упустил ?
Аватара пользователя
Admin
Администратор
Сообщения: 3959
Зарегистрирован: 05 Июль 2005, 15:59
Откуда: Хабаровск
Благодарил (а): 25 раз
Поблагодарили: 22 раза
Контактная информация:

CW10: обработка бинарных файлов

Сообщение Admin »

Ну сходу: Я же кидал новый код в котором избавлялся от memcpy. Сильно лишняя операция
Рай совершает ошибки ничуть не реже чем ад. Просто у него хорошая пресса
kreator
✯ Ветеран ✯
Сообщения: 4983
Зарегистрирован: 28 Май 2009, 15:54
Откуда: Москва
Благодарил (а): 7 раз
Поблагодарили: 20 раз

CW10: обработка бинарных файлов

Сообщение kreator »

Интересно, а сколько бы вы хотели времени? Если речь идёт о таком размере, то несколько минут - это нормально. Можно подёргаться с открытием в монопольном режиме, поюзать функции Stream, Flush ... Можно WinApi-шными поработать. Я как-то проверял скорость драйверов DOS и ASCII, нормально отрабатывает по скорости, WinAPI не нужен.
We are hard at work… for you. :)
Yufil
Ветеран движения
Сообщения: 1277
Зарегистрирован: 16 Май 2006, 14:34
Контактная информация:

CW10: обработка бинарных файлов

Сообщение Yufil »

Мне кажется или на самом деле так? Файл пишется по 1 байту ?
Надо размер записи файла увеличить, тогда скорость вырастет на порядки

Код: Выделить всё

Cstr.SaveToFile   Procedure(String FileName)                  
Loc:FileName      Cstring(260),Static
Out               File,Driver('DOS','/QuickScan=on'),Name(Loc:FileName),Pre(Out),Create
Record            Record 
S                 String(10000)
                  End
                  End
Err               Long                   
RecSize           Long
Pos               Long
                  Code
                  Loc:Filename=lower(FileName)
                  Create(Out)
                  Err=ErrorCode() 
                  If Err
                    Return(Err)
                  End   
                  Open(Out)
                  Err=ErrorCode() 
                  If Err
                    Return(Err)
                  End   
                  Loop Pos=1 to Self.Len By Size(Out:S)
                    RecSize=Size(Out:S)
                    If Pos+RecSize>Self.Len+1
                       RecSize=Self.Len-Pos+1
                    End   
                    MemMove(Address(Out:S),Address(Self.S)+Pos-1,RecSize) 
                    Add(Out,RecSize)
                    Err=ErrorCode() 
                    If Err
                      Close(Out)                 
                      Return(Err)
                    End   
                  End 
                  Close(Out)
                  Return(0) 
SuperMax
Посетитель
Сообщения: 47
Зарегистрирован: 25 Август 2015, 3:22
Поблагодарили: 2 раза

CW10: обработка бинарных файлов

Сообщение SuperMax »

Admin писал(а): Ну сходу: Я же кидал новый код в котором избавлялся от memcpy. Сильно лишняя операция
я помню, но практика показала что обработка массива при помощи memcpy отнимает секунды
тогда как файловая операция гораздо больше
kreator писал(а): Интересно, а сколько бы вы хотели времени? Если речь идёт о таком размере, то несколько минут - это нормально.
я тоже считаю 100МБ в минуту вполне нормальная скорость, но мне стало интересно - для образования
(была бы задача постоянная - я бы ее на СИ и под nix решал)
Аватара пользователя
Игорь Столяров
Ветеран движения
Сообщения: 7378
Зарегистрирован: 07 Июль 2005, 10:19
Откуда: г. Ростов-на-ДоМу
Благодарил (а): 13 раз
Поблагодарили: 48 раз

CW10: обработка бинарных файлов

Сообщение Игорь Столяров »

SuperMax писал(а): надо быстро их считать в память и быстро записать новые
Здесь главные вопрос: надо загружать быстро или с индикацией процесса ?
В WinAPI есть функция ReadFile(), где весь код загрузки бинарного файла в область памяти сводится к трем строкам ...
Аналогично запись. Я не могу сказать про скорость - это надо проводить тестирование, но уж как-то у Вас все сложно ...

Код: Выделить всё


! Вызов
Loc:CodInfo   &STRING
Loc:FileSize   Long
Loc:CodInfo   &= GetFileToString(FileName_,Loc:FileSize)    ! Загрузить файл в строку

! Загрузка файла в строку
GetFileToString      PROCEDURE  (xFileName_,xSize_)

Loc:InFileName       CSTRING(255)
Loc:InFileHandle     ULONG
Loc:SA               GROUP(MS_SECURITY_ATTRIBUTES),PRE()   !
                     END              
Loc:HighSize         ULONG  
Loc:BytesToRead      ULONG
Loc:BytesRead        ULONG(0)
Loc:Info             &STRING   

  CODE
  
  xSize_          = 0           ! Размер загруженного файла
  Loc:BytesToRead = 0    ! Загружено байт из файла
  Loc:Info       &= NULL   ! Буфер загрузки

  Loc:InFileName   = Clip(Left(LongPath(xFileName_))) & '<0>'   ! Открыть файл
  Loc:InFileHandle = CreateFile(Loc:InFileName,80000000H,1h,Loc:SA,3,80h,0000000h)

  If Loc:InFileHandle <> INVALID_HANDLE_VALUE
     Loc:BytesToRead = GetFileSize(Loc:InFileHandle,Loc:HighSize)    ! Получить размер файла
     If Loc:BytesToRead > 0
        Loc:Info &= New(String(Loc:BytesToRead))  ! Создаем буфер для загрузки
        If ~(Loc:Info &= NULL)                    ! Если буфер удачно создан
            If ~ReadFile(Loc:InFileHandle,Address(Loc:Info),Loc:BytesToRead,Address(Loc:BytesRead),0)
                Dispose(Loc:Info)
            elsIf Loc:BytesToRead <> Loc:BytesRead
                Dispose(Loc:Info)
            end
        end
     end
     If ~CloseHandle(Loc:InFileHandle).    ! Закрыть файл
  end

  If ~(Loc:Info &= NULL) then xSize_ = Loc:BytesToRead.
  Return Loc:Info  
Последний раз редактировалось Игорь Столяров 13 Ноябрь 2015, 15:39, всего редактировалось 4 раза.
За теми кто отстал - не возвращаться. (С) Кодекс
Yufil
Ветеран движения
Сообщения: 1277
Зарегистрирован: 16 Май 2006, 14:34
Контактная информация:

CW10: обработка бинарных файлов

Сообщение Yufil »

Кстати, это метод класса CSTR, для работы именно с большими файлами, в том числе бинарными.
Правда, внутри хранится строка типа Cstring, но это почти никогда не мешает.

https://mega.nz/#!RlAilJIT!2hvwWBkAn18R ... J3FmCLcmvw

Тогда грузить и сохранять файлы просто

Код: Выделить всё

S  Cstr 

S.LoadFromFile(InFile)
S.SaveToFile(OutFile) 
S.S[1 : S.Len] - ссылка на содержимое файла
Аватара пользователя
Admin
Администратор
Сообщения: 3959
Зарегистрирован: 05 Июль 2005, 15:59
Откуда: Хабаровск
Благодарил (а): 25 раз
Поблагодарили: 22 раза
Контактная информация:

CW10: обработка бинарных файлов

Сообщение Admin »

Вот гигабайт через память прогоняет и обратно записывает за 10 секунд. На SSD. Ничего не оптимизировал.

Код: Выделить всё

        PROGRAM
        PRAGMA('link(C%V%DOS%X%%L%.LIB)')                           
        MAP
          module('runtime')
            malloc(signed),long,name('_malloc')
            free(long),name('_free')
            memcpy(Long,Long,Unsigned),Name('_memcpy')
          end        
        END
        
file_in     STRING(255)       
size1mb     EQUATE(1024*1024)
FileSize    LONG
Offset      LONG(1)                   
SizeToRead  LONG     
ProgressNum LONG
mem         LONG

InFile  FILE,DRIVER('DOS'),NAME(file_in),PRE(FIN),CREATE
          RECORD
Buffer      STRING(size1mb)
          END
        END

progress    REAL(0)
window WINDOW('Обработка файла'),AT(,,340,113),FONT('MS Sans Serif',8,,FONT:bold),CENTER,GRAY,DOUBLE,SYSTEM
       PROGRESS,USE(?progress),AT(37,49,263,15),RANGE(0,100)
     END

  CODE
  OPEN(window)
  ACCEPT
    CASE EVENT()
    OF EVENT:OpenWindow

      ! read process
      TARGET{PROP:Text} = 'Чтение файла'
      file_in = 'data.dat'
      OPEN(InFile)
      IF ERRORCODE() 
        MESSAGE(ERROR())
        RETURN
      END  
      FileSize = bytes(InFile)                                 
      if mem>0 AND FileSize>0
        free(mem)
      END
      mem = malloc(FileSize)
      IF mem = ''                         
        MESSAGE('Insufficient memory available for ' & FileSize & ' bytes','ERROR',ICON:Hand)                
        RETURN                                    
      END        
      ProgressNum = FileSize/100                       
      LOOP      
        IF FileSize > Offset+size1mb
          SizeToRead = size1mb
        ELSE
          SizeToRead = ABS(Offset-FileSize)+1
        END               
        GET(InFile, Offset, SizeToRead)
        memcpy(mem+Offset-1, ADDRESS(FIN:buffer), SizeToRead)
        Offset += SizeToRead
        ?progress{PROP:Progress} = Offset / ProgressNum
        DISPLAY(?progress)
      UNTIL Offset >= FileSize
      CLOSE(InFile)            
      
      ! write process                                  
      TARGET{PROP:Text} = 'Запись файла'
      ?progress{PROP:Progress} = 0
      DISPLAY(?progress)
      file_in = 'data.out'
      CREATE(InFile)
      OPEN(InFile)
      Offset = 0
      LOOP 
        IF FileSize > Offset+size1mb
          SizeToRead = size1mb
        ELSE
          SizeToRead = ABS(Offset-FileSize) 
        END               
        memcpy(ADDRESS(FIN:buffer),mem+Offset, SizeToRead)
        PUT(InFile, Offset+1, SizeToRead)
        Offset += SizeToRead
        ?progress{PROP:Progress} = Offset / ProgressNum
        DISPLAY(?progress)
      UNTIL Offset >= FileSize
      CLOSE(InFile)
      ! end
      free(mem)
    END
  END
  CLOSE(window)
Рай совершает ошибки ничуть не реже чем ад. Просто у него хорошая пресса
Аватара пользователя
Admin
Администратор
Сообщения: 3959
Зарегистрирован: 05 Июль 2005, 15:59
Откуда: Хабаровск
Благодарил (а): 25 раз
Поблагодарили: 22 раза
Контактная информация:

CW10: обработка бинарных файлов

Сообщение Admin »

Простите. Код корявый. Программист с меня аховый :)

P.S. Просто на чужой смотрю и понимаю что я тракторист :idied:
Рай совершает ошибки ничуть не реже чем ад. Просто у него хорошая пресса
SuperMax
Посетитель
Сообщения: 47
Зарегистрирован: 25 Август 2015, 3:22
Поблагодарили: 2 раза

CW10: обработка бинарных файлов

Сообщение SuperMax »

Admin писал(а): Вот гигабайт через память прогоняет и обратно записывает за 10 секунд. На SSD. Ничего не оптимизировал.
Круто!!!
просто летает!
gopstop2007
✯ Ветеран ✯
Сообщения: 1702
Зарегистрирован: 25 Март 2009, 21:55
Благодарил (а): 9 раз
Поблагодарили: 4 раза

CW10: обработка бинарных файлов

Сообщение gopstop2007 »

может SSD просто кеширует , а не реально пишет :) надо стирать одно, а писать другое :)
“Есть всего 2 типа языков: те, на которые все жалуются и те, которыми никто не пользуется.” — Бьерн Страуструп
Аватара пользователя
Игорь Столяров
Ветеран движения
Сообщения: 7378
Зарегистрирован: 07 Июль 2005, 10:19
Откуда: г. Ростов-на-ДоМу
Благодарил (а): 13 раз
Поблагодарили: 48 раз

CW10: обработка бинарных файлов

Сообщение Игорь Столяров »

gopstop2007 писал(а): может SSD просто кеширует
Любой тест файловых операций аппаратно-зависим ... Тестировать старый и новый код, нужно в одинаковых условиях.
У меня, например RAM диск 8GB + игровая память.
Думаю, что тест который показал на обычном диске минуту, на SSD - 10 сек, на этом RAM диске управится за 1-2 сек.... :)
Т.е. цифры для сравнительного анализа производительности кода вообще ни о чем.
За теми кто отстал - не возвращаться. (С) Кодекс
SuperMax
Посетитель
Сообщения: 47
Зарегистрирован: 25 Август 2015, 3:22
Поблагодарили: 2 раза

CW10: обработка бинарных файлов

Сообщение SuperMax »

у меня на обычном диске
файл 1,163,009,174
чтение 3сек
запись 6 сек
Аватара пользователя
Admin
Администратор
Сообщения: 3959
Зарегистрирован: 05 Июль 2005, 15:59
Откуда: Хабаровск
Благодарил (а): 25 раз
Поблагодарили: 22 раза
Контактная информация:

CW10: обработка бинарных файлов

Сообщение Admin »

SuperMax писал(а): чтение 3сек
запись 6 сек
Найду в помойке свой винт, верну назад :)
Рай совершает ошибки ничуть не реже чем ад. Просто у него хорошая пресса
Ответить