Jak tanio uzyskać liczbę wierszy dużego pliku w Pythonie?

Muszę uzyskać liczbę linii dużego pliku (setek tysięcy linii) w Pythonie. Jaki jest najbardziej efektywny sposób zarówno pamięci, jak i czasu?

W tej chwili robię:

def file_len(fname):
    with open(fname) as f:
        for i, l in enumerate(f):
            pass
    return i + 1

Czy można zrobić coś lepszego?

Author: smci, 2009-05-10

30 answers

Nic lepszego nie osiągniesz.

W końcu każde rozwiązanie będzie musiało odczytać cały plik, dowiedzieć się, ile masz \n i zwrócić wynik.

Czy masz lepszy sposób na zrobienie tego bez czytania całego pliku? Nie jestem pewien... Najlepszym rozwiązaniem zawsze będzie I / O-bound, najlepsze co możesz zrobić, to upewnić się, że nie używasz niepotrzebnej pamięci, ale wygląda na to, że masz to pokryte.

 385
Author: Yuval Adam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-10 10:55:53

Jedna linijka, pewnie dość szybka:

num_lines = sum(1 for line in open('myfile.txt'))
 669
Author: Kyle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-19 19:07:06

Wierzę, że plik mapowany pamięcią będzie najszybszym rozwiązaniem. Wypróbowałem cztery funkcje: funkcję wysłaną przez OP (opcount); prostą iterację nad liniami w pliku (simplecount); readline ze złożeniem mapowanym pamięcią (mmap) (mapcount); i rozwiązanie do odczytu bufora oferowane przez Mykola Kharechko (bufcount).

Uruchomiłem każdą funkcję pięć razy i obliczyłem średni czas wykonania dla 1,2 miliona plików tekstowych.

Windows XP, Python 2.5, 2GB RAM, 2 GHz AMD procesor

Oto moje wyniki:

mapcount : 0.465599966049
simplecount : 0.756399965286
bufcount : 0.546800041199
opcount : 0.718600034714

Edit : liczby dla Pythona 2.6:

mapcount : 0.471799945831
simplecount : 0.634400033951
bufcount : 0.468800067902
opcount : 0.602999973297

Więc strategia odczytu bufora wydaje się być najszybsza Dla Windows / Python 2.6

Oto kod:

from __future__ import with_statement
import time
import mmap
import random
from collections import defaultdict

def mapcount(filename):
    f = open(filename, "r+")
    buf = mmap.mmap(f.fileno(), 0)
    lines = 0
    readline = buf.readline
    while readline():
        lines += 1
    return lines

def simplecount(filename):
    lines = 0
    for line in open(filename):
        lines += 1
    return lines

def bufcount(filename):
    f = open(filename)                  
    lines = 0
    buf_size = 1024 * 1024
    read_f = f.read # loop optimization

    buf = read_f(buf_size)
    while buf:
        lines += buf.count('\n')
        buf = read_f(buf_size)

    return lines

def opcount(fname):
    with open(fname) as f:
        for i, l in enumerate(f):
            pass
    return i + 1


counts = defaultdict(list)

for i in range(5):
    for func in [mapcount, simplecount, bufcount, opcount]:
        start_time = time.time()
        assert func("big_file.txt") == 1209138
        counts[func].append(time.time() - start_time)

for key, vals in counts.items():
    print key.__name__, ":", sum(vals) / float(len(vals))
 215
Author: Ryan Ginstrom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-12 05:37:02

Musiałem opublikować to na podobne pytanie, dopóki mój wynik reputacji nie skoczył trochę (dzięki temu, kto mnie uderzył!).

Wszystkie te rozwiązania ignorują jeden sposób, aby działać znacznie szybciej, a mianowicie poprzez użycie interfejsu niebuforowanego (raw), użycie bytearrays i własne buforowanie. (Dotyczy to tylko Pythona 3. W Pythonie 2 interfejs surowy może być domyślnie używany, ale w Pythonie 3 domyślnie będzie używany w Unicode.)

Używanie zmodyfikowanej wersji czasu narzędzie, uważam, że poniższy kod jest szybszy (i nieznacznie bardziej pythoniczny) niż którekolwiek z oferowanych rozwiązań:

def rawcount(filename):
    f = open(filename, 'rb')
    lines = 0
    buf_size = 1024 * 1024
    read_f = f.raw.read

    buf = read_f(buf_size)
    while buf:
        lines += buf.count(b'\n')
        buf = read_f(buf_size)

    return lines

Używając oddzielnej funkcji generatora, uruchamia się to nieco szybciej:

def _make_gen(reader):
    b = reader(1024 * 1024)
    while b:
        yield b
        b = reader(1024*1024)

def rawgencount(filename):
    f = open(filename, 'rb')
    f_gen = _make_gen(f.raw.read)
    return sum( buf.count(b'\n') for buf in f_gen )

Można to zrobić całkowicie za pomocą wyrażeń generatorów w linii za pomocą itertools, ale robi się to dość dziwnie wyglądające:

from itertools import (takewhile,repeat)

def rawincount(filename):
    f = open(filename, 'rb')
    bufgen = takewhile(lambda x: x, (f.raw.read(1024*1024) for _ in repeat(None)))
    return sum( buf.count(b'\n') for buf in bufgen )

Oto moje czasy:

function      average, s  min, s   ratio
rawincount        0.0043  0.0041   1.00
rawgencount       0.0044  0.0042   1.01
rawcount          0.0048  0.0045   1.09
bufcount          0.008   0.0068   1.64
wccount           0.01    0.0097   2.35
itercount         0.014   0.014    3.41
opcount           0.02    0.02     4.83
kylecount         0.021   0.021    5.05
simplecount       0.022   0.022    5.25
mapcount          0.037   0.031    7.46
 155
Author: Michael Bacon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 10:02:23

Możesz wykonać podproces i uruchomić wc -l filename

import subprocess

def file_len(fname):
    p = subprocess.Popen(['wc', '-l', fname], stdout=subprocess.PIPE, 
                                              stderr=subprocess.PIPE)
    result, err = p.communicate()
    if p.returncode != 0:
        raise IOError(err)
    return int(result.strip().split()[0])
 95
Author: Ólafur Waage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-11 12:23:02

Oto program Pythona do wykorzystania biblioteki wieloprocesorowej do dystrybucji liczenia linii między maszynami / rdzeniami. Mój test poprawia zliczanie 20 milionów plików liniowych z 26 sekund do 7 sekund przy użyciu 8-rdzeniowego serwera windows 64. Uwaga: brak mapowania pamięci powoduje, że rzeczy są znacznie wolniejsze.

import multiprocessing, sys, time, os, mmap
import logging, logging.handlers

def init_logger(pid):
    console_format = 'P{0} %(levelname)s %(message)s'.format(pid)
    logger = logging.getLogger()  # New logger at root level
    logger.setLevel( logging.INFO )
    logger.handlers.append( logging.StreamHandler() )
    logger.handlers[0].setFormatter( logging.Formatter( console_format, '%d/%m/%y %H:%M:%S' ) )

def getFileLineCount( queues, pid, processes, file1 ):
    init_logger(pid)
    logging.info( 'start' )

    physical_file = open(file1, "r")
    #  mmap.mmap(fileno, length[, tagname[, access[, offset]]]

    m1 = mmap.mmap( physical_file.fileno(), 0, access=mmap.ACCESS_READ )

    #work out file size to divide up line counting

    fSize = os.stat(file1).st_size
    chunk = (fSize / processes) + 1

    lines = 0

    #get where I start and stop
    _seedStart = chunk * (pid)
    _seekEnd = chunk * (pid+1)
    seekStart = int(_seedStart)
    seekEnd = int(_seekEnd)

    if seekEnd < int(_seekEnd + 1):
        seekEnd += 1

    if _seedStart < int(seekStart + 1):
        seekStart += 1

    if seekEnd > fSize:
        seekEnd = fSize

    #find where to start
    if pid > 0:
        m1.seek( seekStart )
        #read next line
        l1 = m1.readline()  # need to use readline with memory mapped files
        seekStart = m1.tell()

    #tell previous rank my seek start to make their seek end

    if pid > 0:
        queues[pid-1].put( seekStart )
    if pid < processes-1:
        seekEnd = queues[pid].get()

    m1.seek( seekStart )
    l1 = m1.readline()

    while len(l1) > 0:
        lines += 1
        l1 = m1.readline()
        if m1.tell() > seekEnd or len(l1) == 0:
            break

    logging.info( 'done' )
    # add up the results
    if pid == 0:
        for p in range(1,processes):
            lines += queues[0].get()
        queues[0].put(lines) # the total lines counted
    else:
        queues[0].put(lines)

    m1.close()
    physical_file.close()

if __name__ == '__main__':
    init_logger( 'main' )
    if len(sys.argv) > 1:
        file_name = sys.argv[1]
    else:
        logging.fatal( 'parameters required: file-name [processes]' )
        exit()

    t = time.time()
    processes = multiprocessing.cpu_count()
    if len(sys.argv) > 2:
        processes = int(sys.argv[2])
    queues=[] # a queue for each process
    for pid in range(processes):
        queues.append( multiprocessing.Queue() )
    jobs=[]
    prev_pipe = 0
    for pid in range(processes):
        p = multiprocessing.Process( target = getFileLineCount, args=(queues, pid, processes, file_name,) )
        p.start()
        jobs.append(p)

    jobs[0].join() #wait for counting to finish
    lines = queues[0].get()

    logging.info( 'finished {} Lines:{}'.format( time.time() - t, lines ) )
 45
Author: Martlark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 10:59:42

Jednolinijkowe rozwiązanie bash podobne do tej odpowiedzi , przy użyciu nowoczesnej funkcji subprocess.check_output:

def line_count(filename):
    return int(subprocess.check_output(['wc', '-l', filename]).split()[0])
 25
Author: 1'',
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-13 22:11:01

Użyłbym metody Pythona file object readlines, w następujący sposób:

with open(input_file) as foo:
    lines = len(foo.readlines())

Otwiera plik, tworzy listę linii w pliku, zlicza długość listy, zapisuje ją do zmiennej i ponownie zamyka plik.

 15
Author: Daniel Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-08 12:46:12

To najszybsza rzecz jaką znalazłem używając czystego Pythona. Możesz użyć dowolnej ilości pamięci, ustawiając bufor, chociaż 2* * 16 wydaje się być słodkim miejscem na moim komputerze.

from functools import partial

buffer=2**16
with open(myfile) as f:
        print sum(x.count('\n') for x in iter(partial(f.read,buffer), ''))

Znalazłem odpowiedź tutaj Dlaczego czytanie linii ze standardowego wejścia jest znacznie wolniejsze w C++ niż w Pythonie? i trochę go podkręciłem. Jest to bardzo dobra lektura, aby zrozumieć, jak szybko liczyć linie, choć wc -l jest nadal o 75% szybszy niż cokolwiek innego.

 13
Author: jeffpkamp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:20
def file_len(full_path):
  """ Count number of lines in a file."""
  f = open(full_path)
  nr_of_lines = sum(1 for line in f)
  f.close()
  return nr_of_lines
 12
Author: pkit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-10 10:33:41

Oto co używam, wydaje się całkiem czyste:

import subprocess

def count_file_lines(file_path):
    """
    Counts the number of lines in a file using wc utility.
    :param file_path: path to file
    :return: int, no of lines
    """
    num = subprocess.check_output(['wc', '-l', file_path])
    num = num.split(' ')
    return int(num[0])

Aktualizacja: jest to nieznacznie szybsze niż używanie czystego Pythona, ale kosztem użycia pamięci. Podproces rozwidla nowy proces z takim samym śladem pamięci jak proces macierzysty podczas wykonywania polecenia.

 11
Author: radtek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-25 15:30:28

Rozwiązanie jednej linii:

import os
os.system("wc -l  filename")  

Mój fragment:

>>> os.system('wc -l *.txt')

0 bar.txt
1000 command.txt
3 test_file.txt
1003 total
 9
Author: TheExorcist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-09 18:53:36

Mam małą (4-8%) poprawę z tej wersji, która ponownie wykorzystuje stały bufor, więc powinien unikać pamięci lub GC napowietrznych:

lines = 0
buffer = bytearray(2048)
with open(filename) as f:
  while f.readinto(buffer) > 0:
      lines += buffer.count('\n')

Możesz pobawić się rozmiarem bufora i być może zauważysz małą poprawę.

 8
Author: Scott Persinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-25 19:31:53

Odpowiedź Kyle ' a

num_lines = sum(1 for line in open('my_file.txt'))

Jest prawdopodobnie najlepszy, alternatywą dla tego jest

num_lines =  len(open('my_file.txt').read().splitlines())

Oto porównanie wydajności obu

In [20]: timeit sum(1 for line in open('Charts.ipynb'))
100000 loops, best of 3: 9.79 µs per loop

In [21]: timeit len(open('Charts.ipynb').read().splitlines())
100000 loops, best of 3: 12 µs per loop
 8
Author: ChillarAnand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:28

Aby uzupełnić powyższe metody wypróbowałem wariant z modułem fileinput:

import fileinput as fi   
def filecount(fname):
        for line in fi.input(fname):
            pass
        return fi.lineno()

I przekazał plik 60mil linii do wszystkich wyżej wymienionych metod:

mapcount : 6.1331050396
simplecount : 4.588793993
opcount : 4.42918205261
filecount : 43.2780818939
bufcount : 0.170812129974

To dla mnie małe zaskoczenie, że fileinput jest tak zły i skaluje się znacznie gorzej niż wszystkie inne metody...

 5
Author: BandGap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-05 11:48:21

Jak dla mnie ten wariant będzie najszybszy:

#!/usr/bin/env python

def main():
    f = open('filename')                  
    lines = 0
    buf_size = 1024 * 1024
    read_f = f.read # loop optimization

    buf = read_f(buf_size)
    while buf:
        lines += buf.count('\n')
        buf = read_f(buf_size)

    print lines

if __name__ == '__main__':
    main()

Powody: buforowanie jest szybsze niż odczyt linii po linii i string.count jest również bardzo szybkie

 5
Author: Mykola Kharechko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-30 16:43:40

Zmodyfikowałem przypadek bufora tak:

def CountLines(filename):
    f = open(filename)
    try:
        lines = 1
        buf_size = 1024 * 1024
        read_f = f.read # loop optimization
        buf = read_f(buf_size)

        # Empty file
        if not buf:
            return 0

        while buf:
            lines += buf.count('\n')
            buf = read_f(buf_size)

        return lines
    finally:
        f.close()

Teraz liczone są również puste pliki i ostatnia linia (bez \n).

 4
Author: Dummy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-25 14:55:52

Ten kod jest krótszy i jaśniejszy. To chyba najlepszy sposób:

num_lines = open('yourfile.ext').read().count('\n')
 4
Author: Texom512,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-23 18:38:13

Wynikiem otwarcia pliku jest iterator, który można przekonwertować do sekwencji o długości:

with open(filename) as f:
   return len(list(f))

Jest to bardziej zwięzłe niż Twoja Jawna pętla i unika enumerate.

 2
Author: Andrew Jaffe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-10 11:40:34

count = max(enumerate(open(filename)))[0]

 2
Author: pyanon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-11 21:09:52
print open('file.txt', 'r').read().count("\n") + 1
 2
Author: Andrés Torres,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-21 06:10:30
def line_count(path):
    count = 0
    with open(path) as lines:
        for count, l in enumerate(lines, start=1):
            pass
    return count
 2
Author: mdwhatcott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-02 21:45:10

Jeśli ktoś chce tanio uzyskać liczbę linii w Pythonie w Linuksie, polecam tę metodę:

import os
print os.popen("wc -l file_path").readline().split()[0]

File_path może być zarówno abstrakcyjną ścieżką do pliku, jak i ścieżką względną. Mam nadzieję, że to pomoże.

 2
Author: Lerner Zhang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-28 09:25:44
def count_text_file_lines(path):
    with open(path, 'rt') as file:
        line_count = sum(1 for _line in file)
    return line_count
 2
Author: jciloa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-17 14:50:35

Co z tym

def file_len(fname):
  counts = itertools.count()
  with open(fname) as f: 
    for _ in f: counts.next()
  return counts.next()
 1
Author: odwl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-10 18:20:28

Co ty na to?

import fileinput
import sys

counter=0
for line in fileinput.input([sys.argv[1]]):
    counter+=1

fileinput.close()
print counter
 1
Author: leba-lev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-19 15:55:39

A może ten jeden-liner:

file_length = len(open('myfile.txt','r').read().split('\n'))

Trwa 0.003 sek używając tej metody do czasu na pliku linii 3900

def c():
  import time
  s = time.time()
  file_length = len(open('myfile.txt','r').read().split('\n'))
  print time.time() - s
 1
Author: onetwopunch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-03 00:55:36

To jest meta-komentarz do niektórych innych odpowiedzi.

  • Techniki odczytu linii i buforowania \n nie zwrócą tej samej odpowiedzi dla każdego pliku, ponieważ niektóre pliki tekstowe nie mają nowej linii na końcu ostatniej linii. Możesz obejść to, sprawdzając ostatni bajt ostatniego bufora nonempty i dodając 1, jeśli nie jest b'\n'.

  • W Pythonie 3 otwarcie pliku w trybie tekstowym i w trybie binarnym może przynieść różne wyniki, ponieważ tryb tekstowy przez domyślnie rozpoznaje CR, LF i CRLF jako zakończenia linii (konwertując je wszystkie na '\n'), podczas gdy w trybie binarnym tylko LF i CRLF będą liczone, jeśli zliczysz b'\n'. Dotyczy to zarówno odczytu wierszy, jak i bufora o stałym rozmiarze. Klasyczny Mac OS używał CR jako końcówki linii; Nie wiem, jak często te pliki są w dzisiejszych czasach.

  • Metoda odczytu bufora wykorzystuje ograniczoną ilość pamięci RAM niezależnie od rozmiaru pliku, podczas gdy Metoda odczytu liniowego może odczytać cały plik do pamięci RAM od razu w najgorszym przypadku(szczególnie jeśli plik używa zakończeń linii CR). W najgorszym przypadku może on użyć znacznie więcej pamięci RAM niż Rozmiar Pliku, ze względu na dynamiczną zmianę rozmiaru bufora linii i (jeśli został otwarty w trybie tekstowym) dekodowanie i przechowywanie Unicode.

  • Możesz poprawić zużycie pamięci i prawdopodobnie szybkość buforowanego podejścia, wstępnie przydzielając bajtearray i używając readinto zamiast read. Jedna z istniejących odpowiedzi (z kilkoma głosami) to robi, ale jest buggy (dwukrotnie liczy kilka bajtów).

  • Odpowiedź odczytu górnego bufora używa dużego bufora (1 MiB). Korzystanie z mniejszego bufora może być w rzeczywistości szybsze ze względu na odczyt systemu operacyjnego. Jeśli odczytasz 32K lub 64K na raz, system operacyjny prawdopodobnie zacznie odczytywać następne 32K/64K do pamięci podręcznej, zanim o to poprosisz, a każda podróż do jądra powróci niemal natychmiast. Jeśli czytasz 1 MiB na raz, system operacyjny raczej nie odczyta całego megabajta. Może to być mniejsza kwota ale nadal będziesz spędzać znaczną ilość czasu siedząc w jądrze i czekając, aż dysk zwróci resztę danych.

 1
Author: benrg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-14 17:52:10

Już wiele odpowiedzi, ale niestety większość z nich to tylko małe gospodarki na ledwo optymalny problem...

Pracowałem nad kilkoma projektami, w których licznik linii był podstawową funkcją oprogramowania, a praca tak szybko, jak to możliwe z ogromną liczbą plików miała ogromne znaczenie.

Głównym wąskim gardłem z liczbą linii jest dostęp We / Wy, ponieważ musisz odczytać każdą linię, aby wykryć znak powrotu linii, po prostu nie ma możliwości obejścia. Drugi potencjał wąskie gardło to zarządzanie pamięcią: im więcej ładujesz jednocześnie, tym szybciej możesz przetwarzać, ale to wąskie gardło jest znikome w porównaniu z pierwszym.

Stąd istnieją 3 główne sposoby na skrócenie czasu przetwarzania funkcji liczenia linii, oprócz drobnych optymalizacji, takich jak wyłączanie kolekcji gc i inne sztuczki z mikro-zarządzaniem:

  1. Rozwiązanie sprzętowe: główny i najbardziej oczywisty sposób jest nie-programowy: kup bardzo szybki dysk SSD / flash. Jak na razie jest to jak można uzyskać największe przyspieszenie.

  2. Rozwiązanie przygotowania danych: jeśli generujesz lub możesz modyfikować sposób generowania przetwarzanych plików, lub jeśli jest dopuszczalne, że możesz je wstępnie przetworzyć, najpierw przekonwertuj wiersz return to unix style (\n), ponieważ spowoduje to zapisanie 1 znaku w porównaniu ze stylami Windows lub MacOS( nie jest to duży zapis, ale jest to łatwy zysk), a po drugie i najważniejsze, możesz potencjalnie pisać wiersze o stałej długości. Jeśli potrzebujesz zmiennej długość, zawsze możesz podkładać mniejsze linie. W ten sposób możesz natychmiast obliczyć liczbę linii z całkowitego rozmiaru pliku, do którego dostęp jest znacznie szybszy. Często najlepszym rozwiązaniem problemu jest jego wstępne przetworzenie, aby lepiej pasował do celu końcowego.

  3. Paralelizacja + rozwiązanie sprzętowe: jeśli możesz kupić wiele dysków twardych( i jeśli to możliwe dyski SSD flash), możesz nawet przekroczyć prędkość jednego dysku, wykorzystując równoległość, przechowując swój dysk pliki w sposób zrównoważony (najłatwiej jest balansować według całkowitej wielkości) pomiędzy dyskami, a następnie odczytywać równolegle ze wszystkich tych dysków. Następnie możesz spodziewać się zwiększenia mnożnika proporcjonalnie do liczby posiadanych dysków. Jeśli kupowanie wielu dysków nie jest dla Ciebie opcją, równoległość prawdopodobnie nie pomoże (z wyjątkiem sytuacji, gdy dysk ma wiele nagłówków odczytu, takich jak niektóre dyski profesjonalnej klasy, ale nawet wtedy wewnętrzna pamięć podręczna dysku i obwody PCB będą prawdopodobnie wąskim gardłem i uniemożliwić pełne użycie wszystkich głowic równolegle, a także musisz opracować konkretny kod dla tego dysku twardego, którego będziesz używać, ponieważ musisz znać dokładne mapowanie klastrów, aby przechowywać pliki na klastrach pod różnymi głowicami i abyś mógł je odczytać z różnymi głowicami po). Rzeczywiście, powszechnie wiadomo, że odczyt sekwencyjny jest prawie zawsze szybszy niż odczyt losowy, a równoległość na pojedynczym dysku będzie miała wydajność bardziej podobną do odczytu losowego niż odczyt sekwencyjny (prędkość dysku twardego można sprawdzić w obu aspektach, na przykład za pomocą CrystalDiskMark).

Jeśli żadna z tych opcji nie jest dostępna, możesz polegać tylko na sztuczkach mikro-zarządzających, aby poprawić o kilka procent prędkość funkcji liczenia linii, ale nie oczekuj niczego naprawdę znaczącego. Możesz raczej oczekiwać, że czas, który poświęcisz na ulepszanie, będzie nieproporcjonalny w porównaniu ze zwrotami z poprawy prędkości, które zobaczysz.

 1
Author: gaborous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-09 19:18:15

Dlaczego nie odczytać pierwszych 100 i ostatnich 100 linii i oszacować średnią długość linii, a następnie podzielić całkowity Rozmiar Pliku przez te liczby? Jeśli nie potrzebujesz dokładnej wartości, może to zadziałać.

 0
Author: Georg Schölly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-10 18:36:20