It seems that the string operation is very slow if we have special characters in the string. Consider this example,
str =
StringJoin@
RandomChoice[Join[Alphabet[], ToString /@ Range[0, 9]], 1000000];
str2 = StringJoin[str, "â"];
sampleCorpus[corpus_, len_, num_] := Module[
{positions =
RandomInteger[{len + 1, StringLength[corpus] - 1}, num]},
Thread[
StringTake[corpus, {# - len, #} & /@ positions] ->
StringPart[corpus, positions + 1]]];
trainingData = sampleCorpus[str, 25, 10000]; // AbsoluteTiming
(* {0.020949, Null} *)
trainingData = sampleCorpus[str2, 25, 10000]; // AbsoluteTiming
(* {24.5085, Null} *)
The performance is decreased by a factor of more than 1000X. So why does this happen and is this a bug? I'm very surprised to see this since these string operations are very fundamental in a computer language, and I don't expect it to be so slow. As a comparison, both Python and Java doesn't have this problem.
Python
Here is a naive implementation in python, which doesn't slow down for the string with special characters and it is about 2X faster than the faster one in Mathematica
import random
import string
import time
ls = list(string.ascii_lowercase) + list(string.ascii_uppercase) + [str(x) for x in range(10)]
str1 = "".join([random.choice(ls) for _ in range(1000000)])
str2 = str1 + 'â'
def sampleCorpus(corpus, l, num):
training = []
for _ in range(num):
idx = random.randrange(len(corpus)-l-1)
training.append([corpus[idx:idx+l],corpus[idx+l+1]])
return training
print("timing:")
start = time.time()
training = sampleCorpus(str1,25,10000)
end = time.time()
print("{:3f}s".format(end - start))
print("timing:")
start = time.time()
training = sampleCorpus(str2,25,10000)
end = time.time()
print("{:3f}s".format(end - start))
timing:
0.012459s
timing:
0.012882s
Java
Examples with and without special characters run about the same speed, about 10X faster than the faster version in Mathematica.
import java.util.Random;
public class Hello {
public static void main(String[] args) {
char[] ls = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWZYZ0123456789".toCharArray();
Random rand = new Random();
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 1000000; i++) sb.append(ls[rand.nextInt(ls.length)]);
String str1 = sb.toString();
sb.append('\u00E2');
String str2 = sb.toString();
long start = 0, end = 0;
System.out.println("timing:");
start = System.currentTimeMillis();
String[][] training = sampleCorpus(str1, 25, 10000);
end = System.currentTimeMillis();
System.out.println(end - start);
System.out.println("timing:");
start = System.currentTimeMillis();
training = sampleCorpus(str2, 25, 10000);
end = System.currentTimeMillis();
System.out.println(end - start);
}
private static String[][] sampleCorpus(String corpus, int l, int num) {
String[][] training = new String[num][2];
Random rand = new Random();
for(int i = 0; i < num; i++) {
int idx = rand.nextInt(corpus.length() - l - 1);
training[i][0] = corpus.substring(idx, idx + l);
training[i][1] = corpus.substring(idx, idx + l + 1);
}
return training;
}
}
StringPart
. $\endgroup$