Задумавшись однажды над написанием кое-какого приложения у меня возникла нужда считать количество строк в файлах. Считать быстро. А файлы бывают большими (500–1500 Мб). И, поскольку в тот момент я находился на больничном, возник спортивный интерес проверить, какой язык произведёт сие действо за минимальное время.
Тестовые программы
Итак, были выбраны доступные мне языки:
-
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/
Добавить комментарий