Kamil Dworakowski wrote:
The timing per expression however
showed something useful, 70% of the time was being spent around
determining membership in the known words dictionary (Data.Map). There
are about 30 000 items in the dictionary.

Right... Python uses hashtables while here I have a tree with log n
access time. I did not want to use the Data.HashTable, it would
pervade
my program with IO. The alternative is an ideal hashmap that never
gets
changed. This program creates a dictionary at start which then is only
being used to read from: an ideal application for the Data.PerfectHash
by Mark Wotton available on Hackage [3].

The PerfectHash is a dictionary keyed by ByteStrings, so I had to
migrate the program to use these instead of Strings. Sadly it
increased
the running time to 59s. Adding PerfectHash brought the running time
down to 39s. I have done some code restructuring with the view to
publishing it, which brought the running time up to 45s. The code is
available on patch-tag [4] for anyone to scrutinize and hopefully
point
out mistakes (it uses big.txt and misspellings file which are linked
to
from Norvig's page [1]).

You should also consider using Data.Trie from the bytestring-trie package. It also uses ByteStrings as the keys, but it uses a big-endian patricia tree instead of a hash. This removes Data.Map's duplication of effort for comparing common prefixes of strings. Constructing a Data.Trie can be somewhat slower than constructing a Data.Map ByteString, but the lookup is much faster.

If your misspellings file changes infrequently and you're not afraid of utilities, you can pre-compile the Data.Trie and read it in with Data.Binary. If it changes really infrequently and you're not afraid of recompilation, you can use Template Haskell to store the pre-compiled form directly in the executable.


Frankly, I am not impressed with the PerfectHash performance; It looks
as if it is only two times faster than Data.Map. Is this because of
the number of elements?

Perfection does have its cost.

I haven't played with Data.PerfectHash, but if the hashing function is slow then it will kill your performance. In general hashing will do too much work because it looks at the whole ByteString which is usually unnecessary (though this extra work can be less than the costs of putting the work off, depending on the dataset).

...

Though honestly, most spellcheckers are more interested in optimizing for space rather than optimizing for time. For this, you'll want to look into something like Golomb codes (that is, Huffman codes for exponentially-distributed infinite-alphabets) as was used in the Unix "spell" utility back in the day.

Once you've defined a prefix-free code for all possible words, you can encode each word and pack the result into a ByteString (treated as an array of bits) and hand it off to Data.Trie which will do the right thing. Depending on the size of your data, you may want to squash the Data.Trie down into an array and use an algorithm rather than the datastructure to do your search (just as binary search algorithms on arrays actualize data structures like Data.Map (or datastructures like data.Map reify binary search algorithms, if you prefer)).

--
Live well,
~wren
_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to