2015-07-13 59 views
5

Şu anda Elixir'i öğrenmekle çok ilgileniyorum ve yeni bir dil öğrenmeye yönelik tipik yaklaşımım, basit programlar geliştirmektir.Bu Elixir kodu neden bu kadar yavaş?

defmodule LineMatch do 
    def file(s, path) do 
    case File.open(path, [:read]) do 
     {:ok, fd} -> match s, fd 
     {_, error} -> IO.puts "#{:file.format_error error}" 
    end 
    end 
    defp match(s, fd) do 
    case IO.read fd, :line do 
     {:error, error} -> IO.puts("oh noez! Error: #{error}") 
     line -> match(s, line, fd) 
    end 
    end 
    defp match(s, line, fd) when line !== :eof do 
    if String.contains?(line, s) do 
     IO.write line 
    end 
    match(s, IO.read(fd, :line), fd) 
    end 
    defp match(_, _line, _) when _line === :eof do 
    end 
end 

Bunu yapmanın en zarif yolu büyük olasılıkla değil ve ben:

Yani burada gösterilmektedir bir (çok basit) grep benzeri bir program (veya modül) yazmaya karar Ayrıca işlevsel programlama için oldukça yeni, bu yüzden süper hızlı olmasını beklemiyordum. Ama bu sadece hızlı değil, aslında süper yavaş. O kadar yavaş ki, muhtemelen süper bir hata yaptım.

Herkes bana ne olduğunu ve nasıl daha iyi hale getirebileceğini söyleyebilir mi?

Genellikle Aksine lad2025 yanıtında olduğu gibi bütün dosyasında okuma daha

case System.argv do 
    [searchTerm, path] -> LineMatch.file(searchTerm, path) 
    _ -> IO.puts "Usage: lineMatch searchTerm path" 
end 

cevap

6

gibi dosya ayrı .exs ile kodu test, iki şeyi yaparak iyi performansı elde edebilirsiniz. İlk olarak, dosya satırlarının bir akışını oluşturmak için IO.binstream kullanın, ancak ham ikili olarak (performans için) kullanın. IO.stream öğesinin kullanılması UTF-8 olarak okunduğunda, dosyayı okuduğunuz sırada dönüştürme yapmak için ek maliyet artar. UTF-8 dönüşümüne ihtiyacınız varsa, o zaman yavaş olacaktır. Ayrıca, Stream.filter/2 ve Stream.map/2 işlevlerini kullanarak filtreleme ve eşleme işlemlerini uygulamak, satırları birden çok kez yinelemenizi önler.

defmodule Grepper do 
    def grep(path, str) do 
    case File.open(path) do 
     {:error, reason} -> IO.puts "Error grepping #{path}: #{reason}" 
     {:ok, file} -> 
     IO.binstream(file, :line) 
     |> Stream.filter(&(String.contains?(&1, str))) 
     |> Stream.map(&(IO.puts(IO.ANSI.green <> &1 <> IO.ANSI.reset))) 
     |> Stream.run 
    end 
    end 
end 

Ben senin koduyla büyük sorunu UTF-8 dönüşüm şüpheli, ama bu satırları "itilmiş" için IO.read çağırarak yerine alarak çizgi ile dosya hattından "çekme" tarafından olabilir IO.stream|binstream kullanarak filtreleme/yazdırma işlemleriniz, orada bazı ekstra maliyete neden oluyorsunuz. Emin olmak için Elixir'in kaynağına bakmalıyım, ama yukarıdaki kod makinemde oldukça başarılıydı (Olson timezone veritabanından 143kb dosyası arıyordum, çok büyük boyutlu dosyalar üzerinde nasıl çalışacağından emin değilim) Kullanışlı bir örnek dosyam yok). File.stream kullanılması

+0

Çok ayrıntılı yanıtınız için teşekkür ederiz! Önerinizi kesinlikle deneyeceğim. Örnek veriler oluşturmak için http://www.generatedata.com/ adresini kullandım. Hala bunun neden yavaş olduğunu merak ediyorum. Tabii, UTF-8 dönüşümü yavaşlatıyor ama 500 ms'de bir satır gibi çıktı aldım. – koehr

+0

Gerçekten UTF-8 dönüşüm olduğundan şüpheliyim. Her I/O işlemi iksirdeki işlemler arasında bir mesaj olduğundan, File.read aracılığıyla dosyalarla çalışmak çok yavaştır. Büyük bir parçayı okumak ve sonra bu tekli ikiliyi ayrıştırmak neredeyse her zaman daha hızlıdır. –

1

! çok daha verimli olacak. Bunu deneyin:

defmodule Grepper do 
    def grep(path, str) do 
    File.stream!(path) 
     |> Stream.filter(&(String.contains?(&1, str))) 
     |> Stream.map(&(IO.puts(IO.ANSI.green <> &1 <> IO.ANSI.reset))) 
     |> Stream.run 
    end 
end 
+0

Şimdiye kadar en hızlı. : timer.tc 2600 ila 3000ms, bitwalkerdaki binstream varyantı ise 6400 ile 6900 ms arasında değişiyor. Şimdi bu orijinal yavaşlığın kaynağı olup olmadığını görmek için utf-8 dönüşümüyle oynayacağım. – koehr

+0

Bunun yerine File.steam! (Yol, [: utf8]) kullanmak, hızı hiç değiştirmedi. Hala neden yaptığımı çok yavaşlamıyorum. Görüyorum ki daha yavaş, evet. Her hat çıkışı arasında 500 ms kadar yavaş mı? Gerçekten çok tuhaf – koehr

+0

, hem 'IO.stream' ve' File.stream' etkili aynı şeyi yapmak ('IO.stream' tüm ES girişini kullanmak soyutlanmış, ancak akışı okuma uygulaması temelde aynıdır). Romanın çözümü ile benimki arasında büyük bir fark olması garip ama ben bunu aklımda tutmalıyım. 'File.stream!' Bir yok: utf8 modu, '' raw ''binstream' uygulamasının simule etmek için' File.stream! 'Olarak yapmalıyım, UTF-8 varsayılan olarak ne yapabilirim? görmek. – bitwalker

İlgili konular