Сколько же строк в файле…

от автора

Задумавшись однажды над написанием кое-какого приложения у меня возникла нужда считать количество строк в файлах. Считать быстро. А файлы бывают большими (500–1500 Мб). И, поскольку в тот момент я находился на больничном, возник спортивный интерес проверить, какой язык произведёт сие действо за минимальное время.

TL;DR; Go — победил.

Тестовые программы

Итак, были выбраны доступные мне языки:

  • linesCount.c

    #include <stdio.h> #include <time.h>  int main(int argc, const char * argv[]) {         const char *path = "big.csv";          double startTime = clock();          int linesCount = 0;     FILE *fp = fopen(path, "r");     int bufsize = 32*1024;     char buff[bufsize];     while (fgets(buff, bufsize, fp) != NULL) {         linesCount++;     }     fclose(fp);          double endTime = clock();     double totalTime = (endTime - startTime) / CLOCKS_PER_SEC;     printf("Time: %f s\n", totalTime);     printf("Lines: %d \n", linesCount);          return 0; } 

  • C# (Mono/macOS, .Net/Windows)

    using System; using System.IO;  namespace lineStat { 	class MainClass 	{ 		public static void Main(string[] args) 		{ 			var filename = "big.csv";  			var startTime = DateTime.Now; 			var linesCount = countLinesInFile(filename); 			var spentTime = DateTime.Now - startTime; 			Console.WriteLine("Lines count: {0} in {1}", linesCount, spentTime); 		}  		static int countLinesInFile(string filename) 		{ 			int linesCount = 0; 			using (var reader = new StreamReader(filename)) 			{ 				while (reader.ReadLine() != null) { 					linesCount++; 				} 			} 			return linesCount; 		} 	} }

  • Go

    package main  import ( 	"bytes" 	"fmt" 	"io" 	"os" 	"time" )  func main() {  	start_time := time.Now() 	file, _ := os.Open("big.csv") 	count, _ := lineCounter(file) 	elapsed := time.Since(start_time) 	fmt.Printf("Lines count: %d in %s\n", count, elapsed) }  func lineCounter(r io.Reader) (int, error) { 	buf := make([]byte, 32*1024) 	count := 0 	lineSep := []byte{'\n'}  	for { 		c, err := r.Read(buf) 		count += bytes.Count(buf[:c], lineSep)  		switch { 		case err == io.EOF: 			return count, nil  		case err != nil: 			return count, err 		} 	} }

  • Java

    package me.meamka;  import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.text.DecimalFormat; import java.text.NumberFormat;  public class LinesCount {      public static void main(String[] args) throws IOException { 	// write your code here         String filename = "big.csv";         System.out.println("Count lines in " + filename);         long startTime = System.nanoTime();         int linesCount = countLinesInFile(filename);         long endTime = System.nanoTime();         NumberFormat formatter = new DecimalFormat("#0.00000");         System.out.println("Lines count: " + linesCount +" in " + formatter.format((endTime - startTime) / 1000000000d));     }      private static int countLinesInFile(String filename) throws IOException {         int linesCount = 0;          try (BufferedReader br = new BufferedReader(new FileReader(filename))) {             while (br.readLine() != null) {                 linesCount++;             }         }         return linesCount;     } }
  • PHP

    <?php function countLinesInFile($filename) { 	$f = fopen( 'big.csv', 'r'); 	$lineCount = 0; 	while (!feof($f)) 	{ 		if (fgets($f))         $lineCount++; 	} 	fclose($f); 	return $lineCount; } function main() { 	$start = microtime(true); 	$linesCount = countLinesInFile($filename); 	$duration = number_format(microtime(true)-$start, 4); 	$memory = number_format(memory_get_peak_usage(true),0,'.',' '); 	echo "Total lines: $linesCount; Duration: $duration seconds; Memory used: $memory bytes\n"; } main(); ?>

  • Python

    # coding: utf-8 import time  def linesCount(filename):     count = 0     with open(filename, 'r') as reader:         while reader.readline():             count += 1      return count   if __name__ == '__main__':     filename = 'big.csv'      start_time = time.time()     count = linesCount(filename)     end_time = time.time() - start_time      print("Lines count: %s in %s" % (count, end_time))

  • Swift

    import Foundation import Darwin   var path = "big.csv"  let methodStart = NSDate()  var linesCount = 0 let bufsize = 32*1024 var buf = UnsafeMutablePointer<Int8>.allocate(capacity: bufsize)  let filePointer = fopen(path, "r")  while fgets(buf, Int32(bufsize-1), filePointer) != nil {     //    print(String.fromCString(CString(buf)))     linesCount += 1 } fclose(filePointer)  let methodFinish = Date() let executionTime = methodFinish.timeIntervalSince(methodStart as Date) print("Execution time: \(executionTime)")  print("Lines Count: \(linesCount)")

Тестовая среда

Все действия и тесты (кроме .Net) производились на MacBook Pro 13 Late 2012 с i5 2.5 GHz, SSD 256 Gb и 8 Gb ОЗУ, macOS 10.12.1.

Файл для тестов был найден в виде .csv размером 491,4 Мб и 489286 строк.

Для тестов были написаны простейшие консольные приложения состоящие из 2х функций: собственно, основной функции и функции, которая считает кол-во строк.

Результаты

C ≈ 0.25 сек
C# ≈ 2.8 сек
Go ≈ 0.16 сек
Java ≈ 2.7 сек
PHP ≈ 0.25 сек
Python ≈ 2.6 сек
Objective-C/Swift ≈ 0.25 сек

Оговорюсь, что я не делал большого количества итераций, остановившись на 20. В результаты взял среднее значение.

Из интересного

  • Первые тесты на C# (Mono) показали ужасающие 7.5 секунд, в то время как версия под Windows показала 3 секунды. Полагаю, ОС была чем-то занята, потому как на следующий день тесты выдали терпимые 2.5-3 сек.
  • Никак не ожидал, что будет что-то быстрее C. Go удивил
  • Не ожидал подобную скорость от PHP. Но C-шные вызовы дают о себе знать.
  • Objective-C/Swift благодаря возможности встроить C-код показали аналогичный самому C результат. Я смухлевал

P. S.

Исходные файлы на всякий случай прилеплены в Gist: исходные коды.

Если кто-то желает протестировать и поделиться результатами — милости прошу, мне крайне интересно.

И, повторюсь, интерес у меня крайне спортивный, стоит воздержаться от комментариев «Java — ***, а C — рулит!».

Удачи друзьям груммелей!
ссылка на оригинал статьи https://habrahabr.ru/post/316340/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *