Created
February 11, 2014 18:20
-
-
Save tyano/8940795 to your computer and use it in GitHub Desktop.
MD5ハッシュ突き合わせ(Clojure) - 単体でパフォーマンスチューニングしたものを、core.asyncを使って並列化。引数の末尾に :map をつけると、map/filterを使ったバージョンでもテスト可能。primitiveのloopのほうがちょっとだけ速い。並列化はcore数程度で頭打ち。100とかにしても遅くなるだけ。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns mdsample.core | |
(:require [clojure.core.reducers :as r] | |
[clojure.core.async :refer [chan >! <!! go close! alts!!]]) | |
(:import [java.security MessageDigest] | |
[javax.xml.bind DatatypeConverter] | |
[java.util Arrays] | |
[java.util Queue] | |
[java.util.concurrent ConcurrentLinkedQueue])) | |
(defn- hexdigest | |
[^MessageDigest digester ^String s] | |
(let [d (doto digester | |
(.reset) | |
(.update (.getBytes s)))] | |
(.digest d))) | |
(defn- left-pad [^long num] | |
(let [pad-count (- 6 (count (str num)))] | |
(str (apply str (repeat pad-count \0)) num))) | |
(def ^Queue digester-queue (ConcurrentLinkedQueue.)) | |
(defmacro digester-> [& body] | |
"キューからMessegeDigestを取り出して、それをbodyにスレッディングするマクロです。 | |
キューが空の場合は新しいMessegaDigestを生成します。 | |
使い終わったMessageDigestは、処理終了後にキューに戻されます。 | |
並行処理時にキューを共有するための仕組みです。" | |
`(let [d# (or (.poll digester-queue) | |
(MessageDigest/getInstance "MD5"))] | |
(try | |
(-> d# ~@body) | |
(finally | |
(.add digester-queue d#))))) | |
(defn- find-password-with-map [salt pw ^long start ^long step] | |
"map, filterで検索する" | |
(->> (range start 1000000 step) | |
(map left-pad) | |
(map (fn [six-num] [six-num (digester-> (hexdigest (str salt "$" six-num)))])) | |
(filter #(Arrays/equals ^bytes pw ^bytes (second %))) | |
first)) | |
(defn- find-password-with-loop [salt pw ^long start ^long step] | |
"primitiveのloopで検索する" | |
(loop [i start ret nil] | |
(if (or (> i (long 1000000)) ret) | |
ret | |
(let [six-num (left-pad i) | |
hex (digester-> (hexdigest (str salt "$" six-num)))] | |
(if (Arrays/equals ^bytes pw ^bytes hex) | |
(recur (+ i step) [six-num (DatatypeConverter/printHexBinary hex)]) | |
(recur (+ i step) ret)))))) | |
(defn find-password | |
([salt pw parallel-count type] | |
(.clear digester-queue) | |
(let [pw (DatatypeConverter/parseHexBinary pw) | |
ch (chan) | |
find-fn (case type :map find-password-with-map find-password-with-loop)] | |
(dotimes [i parallel-count] | |
(go (when-let [data (find-fn salt pw (long i) (long parallel-count))] | |
(>! ch data)))) | |
(let [result (<!! ch)] | |
(close! ch) | |
result))) | |
([salt pw parallel-count] | |
(find-password salt pw parallel-count :in))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment