Trochę dziwne, że compare działa wolniej. Gdy szukasz podstringu w całym stringu, to czas wykonywania operacji zależy od rozmiaru obu stringów. Od iloczynu ich rozmiarów. Tymczasem sprawdzenie, czy jeden string zaczyna się od drugiego powinno zależeć liniowo od rozmiaru mniejszego z nich. Różnica na korzyść tego drugiego sposobu jest więc spora, choć może się ujawnić dopiero przy dłuższych stringach.
A próbowałeś użyć operatora [] i sprawdzać to ręcznie litera po literze? Możesz ewentualnie użyć tych c-stringów. Myślę, że ten zwracany przez string::c_str() raczej jest zaimplementowany dość sprawnie (po prostu zwróci wskaźnik do miejsca w pamięci, gdzie klasa przechowuje string), choć z tego co widzę to specyfikacja tego nie gwarantuje.
Tak jak wspomniał ktoś wcześniej, możesz spróbować użyć hashy. Przydaje się to, gdy sprawdzasz który z wielu stringów pasuje do podanego. Hasha dla podanego stringu obliczasz raz, przed pętlą. W każdej iteracji obliczasz hash dla jednego stringu i potem porównujesz tylko hashe. Oszczędza to wielu kosztownych instrukcji porównań (zamiast porównywać litery, porównujesz tylko gotowe hashe, przy czym jeden z nich obliczasz poza pętlą). Funkcja haszująca powinna być jak najbardziej nieskomplikowana. Gdy prawdopodobieństwo wystąpienia kolizji hashów nie jest pomijalne, to robi się tak, że gdy hashe się zgadzają, to wtedy (i tylko wtedy) wykonujesz normalne, pełne porównanie obu stringów. Niemal zawsze powinno wyjść, że stringi się zgadzają, więc to porównanie wykona się tylko raz.
Wydaje mi się jednak, że wystarczy skonstruować ręczne sprawdzenie pierwszych n liter, gdzie n jest liczbą liter podstringu.