It seems like the main improvement/optimization would be not looping twice through the list of all words.  Move *all* processing into the main loop:
my (%word, %gram);
while (<>) {
chomp;
# $_ = lc $_;
/[^a-z]/ and next;
my $sig = pack "C*", sort unpack "C*", $_;
if (exists $word{$sig}) {
if (exists $gram{$sig}) {
next if $gram{$sig} =~ /\b$_\b/;
$gram{$sig} .= " $_"; # rare
}
else {
next if $word{$sig} eq $_;
$gram{$sig} = "$word{$sig} $_"; # rare
}
}
else {
$word{$sig} = $_; # mostly
}
}
print join "\n", (sort values %gram), ''; # just output short list
Only the first word of an anagram set is in both lists.
Here's some more finds, mostly from the short OED from
hereablest bleats stable tables
adroitly dilatory idolatry
angered derange enraged grandee grenade
ascertain cartesian sectarian
asleep elapse please
aspirant partisan
attentive tentative
auctioned cautioned education
canoe ocean
comedian demoniac
compile polemic
covert vector
danger gander garden
deist diets edits idest sited tides
emits items metis mites smite times
emitter termite
lapse leaps pales peals pleas
nastily saintly
obscurantist subtractions
observe obverse verbose
opt pot top
opts post pots spot stop tops
opus soup
oy yo
petrography typographer
peripatetic precipitate
present repents serpent
presume supreme
resin rinse risen siren
salivated validates
slitting stilting tiltings titlings tlingits
views wives
vowels wolves
woodlark workload
 
p