Reduce runtime for manipulating string multiple times using Hashmap / other methods

Multi tool use
Multi tool use












0















The problem statement is that we have a string and we want to rotate each character to its next character i.e a to b, b to c and z to a. Also, the conversion is based on the number x ( x is <= size of string). The number represents the length of the characters to be converted. Let's say the number is 3 and string is stack which means only first 3 characters need to be converted, so output will be tubck. if the number is 5 then the output will be tubdl.



I am running the solution for 100000000 times and generating the variable number randomly. I have solved this problem using 3 approaches mentioned below:




  1. Convert from character to integer, then increment the integer by 1, then convert the integer back to character.

  2. Using the hashmap to get the updated character value in O(1) time.

  3. Using C, but the approach followed is same as of step # 1


The runtime of the hashmap technique (approach 2) is coming to be more. I don't know why?





Approach #1 is



public static void main(String args) {
Long startTime = System.currentTimeMillis();

String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (Integer i = 0; i <100000000; i++) {
{
for (int j = 0; j < random.nextInt(26) + 1; j++) {
if (stringCharArray[j] == 'z') {
stringCharArray[j] = 'a';
} else {
stringCharArray[j] = (char) (((int) (stringCharArray[j])) + 1);
}
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");

}




Approach # 2 using hashmap is



public static void main(String args) {
HashMap hashMap = new HashMap();
hashMap.put('a', 'b');
hashMap.put('b', 'c');
hashMap.put('c', 'd');
hashMap.put('d', 'e');
hashMap.put('e', 'f');
hashMap.put('f', 'g');
hashMap.put('g', 'h');
hashMap.put('h', 'i');
hashMap.put('i', 'j');
hashMap.put('j', 'k');
hashMap.put('k', 'l');
hashMap.put('l', 'm');
hashMap.put('m', 'n');
hashMap.put('n', 'o');
hashMap.put('o', 'p');
hashMap.put('p', 'q');
hashMap.put('q', 'r');
hashMap.put('r', 's');
hashMap.put('s', 't');
hashMap.put('t', 'u');
hashMap.put('u', 'v');
hashMap.put('v', 'w');
hashMap.put('w', 'x');
hashMap.put('x', 'y');
hashMap.put('y', 'z');
hashMap.put('z', 'a');
Long startTime = System.currentTimeMillis();
String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (Integer i = 0; i <100000000; i++) {
{
for (Integer j = 0; j < random.nextInt(26) + 1; j++) {
stringCharArray[j] = (char) hashMap.get(stringCharArray[j]);
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");
}




Approach #3



#include <stdio.h>
#include <time.h>
#include <zconf.h>
#include <stdlib.h>

int main() {
long start = clock();
char name = "abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i <100000000; i++) {
{
for (int j = 0; j < rand() % 25; j++) {
if (name[j] == 'z') {
name[j] = 'a';
} else {
name[j] = (char) (((int) (name[j])) + 1);
}
}
}
}
long stop = clock();
printf("time taken = %ld sec n",( stop-start)/1000);
}




The runtime of approach 1 is ~ 8150 m, approach 2 is ~ 9700 ms and approach 3 is ~ 5400 ms. I am using MacBook Pro 2.3 GHz Intel Core i5 with 8 GB 2133 MHz LPDDR3.





How do I reduce this runtime, I used stream.parallelize() in java for apprach#2 to make the changes but the runtime was increasing. What am I doing wrong?



The goal is to get the runtime less than approach #3. Is there a way in C to parallelize this and solve in lesser time using pthreads?










share|improve this question




















  • 1





    Did you check how much time the programme spent in random functions ? Note also that all approaches are O(1)

    – Damien
    Jan 3 at 8:38








  • 4





    A better approach than the HashMap approach (which necessarily requires boxing) is to populate an array (or even a String): "bcde...xyza".charAt(name[0] - 'a').

    – Andy Turner
    Jan 3 at 8:43








  • 1





    for (int j = 0; j < random.nextInt(26) + 1; j++) will call nextint upon each iteation. I don't think you want that, so have a line before the for loop, e.g. int n=random.nextInt(26)+1; and then for (int j = 0; j < n; j++) This also optimizes the code because it eliminates unnecessary function calls.

    – Paul Ogilvie
    Jan 3 at 9:09








  • 1





    Notice than for (int j = 0; j < rand() % 25; j++) would call rand() at each iteration, you probably want: const int end = rand() % 25; for (int j = 0; j != end; j++).

    – Jarod42
    Jan 3 at 9:10






  • 1





    Instead of using stringCharArray[j], use a pointer variable. This optimizes the code by eliminating indexing instructions.

    – Paul Ogilvie
    Jan 3 at 9:12
















0















The problem statement is that we have a string and we want to rotate each character to its next character i.e a to b, b to c and z to a. Also, the conversion is based on the number x ( x is <= size of string). The number represents the length of the characters to be converted. Let's say the number is 3 and string is stack which means only first 3 characters need to be converted, so output will be tubck. if the number is 5 then the output will be tubdl.



I am running the solution for 100000000 times and generating the variable number randomly. I have solved this problem using 3 approaches mentioned below:




  1. Convert from character to integer, then increment the integer by 1, then convert the integer back to character.

  2. Using the hashmap to get the updated character value in O(1) time.

  3. Using C, but the approach followed is same as of step # 1


The runtime of the hashmap technique (approach 2) is coming to be more. I don't know why?





Approach #1 is



public static void main(String args) {
Long startTime = System.currentTimeMillis();

String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (Integer i = 0; i <100000000; i++) {
{
for (int j = 0; j < random.nextInt(26) + 1; j++) {
if (stringCharArray[j] == 'z') {
stringCharArray[j] = 'a';
} else {
stringCharArray[j] = (char) (((int) (stringCharArray[j])) + 1);
}
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");

}




Approach # 2 using hashmap is



public static void main(String args) {
HashMap hashMap = new HashMap();
hashMap.put('a', 'b');
hashMap.put('b', 'c');
hashMap.put('c', 'd');
hashMap.put('d', 'e');
hashMap.put('e', 'f');
hashMap.put('f', 'g');
hashMap.put('g', 'h');
hashMap.put('h', 'i');
hashMap.put('i', 'j');
hashMap.put('j', 'k');
hashMap.put('k', 'l');
hashMap.put('l', 'm');
hashMap.put('m', 'n');
hashMap.put('n', 'o');
hashMap.put('o', 'p');
hashMap.put('p', 'q');
hashMap.put('q', 'r');
hashMap.put('r', 's');
hashMap.put('s', 't');
hashMap.put('t', 'u');
hashMap.put('u', 'v');
hashMap.put('v', 'w');
hashMap.put('w', 'x');
hashMap.put('x', 'y');
hashMap.put('y', 'z');
hashMap.put('z', 'a');
Long startTime = System.currentTimeMillis();
String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (Integer i = 0; i <100000000; i++) {
{
for (Integer j = 0; j < random.nextInt(26) + 1; j++) {
stringCharArray[j] = (char) hashMap.get(stringCharArray[j]);
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");
}




Approach #3



#include <stdio.h>
#include <time.h>
#include <zconf.h>
#include <stdlib.h>

int main() {
long start = clock();
char name = "abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i <100000000; i++) {
{
for (int j = 0; j < rand() % 25; j++) {
if (name[j] == 'z') {
name[j] = 'a';
} else {
name[j] = (char) (((int) (name[j])) + 1);
}
}
}
}
long stop = clock();
printf("time taken = %ld sec n",( stop-start)/1000);
}




The runtime of approach 1 is ~ 8150 m, approach 2 is ~ 9700 ms and approach 3 is ~ 5400 ms. I am using MacBook Pro 2.3 GHz Intel Core i5 with 8 GB 2133 MHz LPDDR3.





How do I reduce this runtime, I used stream.parallelize() in java for apprach#2 to make the changes but the runtime was increasing. What am I doing wrong?



The goal is to get the runtime less than approach #3. Is there a way in C to parallelize this and solve in lesser time using pthreads?










share|improve this question




















  • 1





    Did you check how much time the programme spent in random functions ? Note also that all approaches are O(1)

    – Damien
    Jan 3 at 8:38








  • 4





    A better approach than the HashMap approach (which necessarily requires boxing) is to populate an array (or even a String): "bcde...xyza".charAt(name[0] - 'a').

    – Andy Turner
    Jan 3 at 8:43








  • 1





    for (int j = 0; j < random.nextInt(26) + 1; j++) will call nextint upon each iteation. I don't think you want that, so have a line before the for loop, e.g. int n=random.nextInt(26)+1; and then for (int j = 0; j < n; j++) This also optimizes the code because it eliminates unnecessary function calls.

    – Paul Ogilvie
    Jan 3 at 9:09








  • 1





    Notice than for (int j = 0; j < rand() % 25; j++) would call rand() at each iteration, you probably want: const int end = rand() % 25; for (int j = 0; j != end; j++).

    – Jarod42
    Jan 3 at 9:10






  • 1





    Instead of using stringCharArray[j], use a pointer variable. This optimizes the code by eliminating indexing instructions.

    – Paul Ogilvie
    Jan 3 at 9:12














0












0








0








The problem statement is that we have a string and we want to rotate each character to its next character i.e a to b, b to c and z to a. Also, the conversion is based on the number x ( x is <= size of string). The number represents the length of the characters to be converted. Let's say the number is 3 and string is stack which means only first 3 characters need to be converted, so output will be tubck. if the number is 5 then the output will be tubdl.



I am running the solution for 100000000 times and generating the variable number randomly. I have solved this problem using 3 approaches mentioned below:




  1. Convert from character to integer, then increment the integer by 1, then convert the integer back to character.

  2. Using the hashmap to get the updated character value in O(1) time.

  3. Using C, but the approach followed is same as of step # 1


The runtime of the hashmap technique (approach 2) is coming to be more. I don't know why?





Approach #1 is



public static void main(String args) {
Long startTime = System.currentTimeMillis();

String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (Integer i = 0; i <100000000; i++) {
{
for (int j = 0; j < random.nextInt(26) + 1; j++) {
if (stringCharArray[j] == 'z') {
stringCharArray[j] = 'a';
} else {
stringCharArray[j] = (char) (((int) (stringCharArray[j])) + 1);
}
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");

}




Approach # 2 using hashmap is



public static void main(String args) {
HashMap hashMap = new HashMap();
hashMap.put('a', 'b');
hashMap.put('b', 'c');
hashMap.put('c', 'd');
hashMap.put('d', 'e');
hashMap.put('e', 'f');
hashMap.put('f', 'g');
hashMap.put('g', 'h');
hashMap.put('h', 'i');
hashMap.put('i', 'j');
hashMap.put('j', 'k');
hashMap.put('k', 'l');
hashMap.put('l', 'm');
hashMap.put('m', 'n');
hashMap.put('n', 'o');
hashMap.put('o', 'p');
hashMap.put('p', 'q');
hashMap.put('q', 'r');
hashMap.put('r', 's');
hashMap.put('s', 't');
hashMap.put('t', 'u');
hashMap.put('u', 'v');
hashMap.put('v', 'w');
hashMap.put('w', 'x');
hashMap.put('x', 'y');
hashMap.put('y', 'z');
hashMap.put('z', 'a');
Long startTime = System.currentTimeMillis();
String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (Integer i = 0; i <100000000; i++) {
{
for (Integer j = 0; j < random.nextInt(26) + 1; j++) {
stringCharArray[j] = (char) hashMap.get(stringCharArray[j]);
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");
}




Approach #3



#include <stdio.h>
#include <time.h>
#include <zconf.h>
#include <stdlib.h>

int main() {
long start = clock();
char name = "abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i <100000000; i++) {
{
for (int j = 0; j < rand() % 25; j++) {
if (name[j] == 'z') {
name[j] = 'a';
} else {
name[j] = (char) (((int) (name[j])) + 1);
}
}
}
}
long stop = clock();
printf("time taken = %ld sec n",( stop-start)/1000);
}




The runtime of approach 1 is ~ 8150 m, approach 2 is ~ 9700 ms and approach 3 is ~ 5400 ms. I am using MacBook Pro 2.3 GHz Intel Core i5 with 8 GB 2133 MHz LPDDR3.





How do I reduce this runtime, I used stream.parallelize() in java for apprach#2 to make the changes but the runtime was increasing. What am I doing wrong?



The goal is to get the runtime less than approach #3. Is there a way in C to parallelize this and solve in lesser time using pthreads?










share|improve this question
















The problem statement is that we have a string and we want to rotate each character to its next character i.e a to b, b to c and z to a. Also, the conversion is based on the number x ( x is <= size of string). The number represents the length of the characters to be converted. Let's say the number is 3 and string is stack which means only first 3 characters need to be converted, so output will be tubck. if the number is 5 then the output will be tubdl.



I am running the solution for 100000000 times and generating the variable number randomly. I have solved this problem using 3 approaches mentioned below:




  1. Convert from character to integer, then increment the integer by 1, then convert the integer back to character.

  2. Using the hashmap to get the updated character value in O(1) time.

  3. Using C, but the approach followed is same as of step # 1


The runtime of the hashmap technique (approach 2) is coming to be more. I don't know why?





Approach #1 is



public static void main(String args) {
Long startTime = System.currentTimeMillis();

String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (Integer i = 0; i <100000000; i++) {
{
for (int j = 0; j < random.nextInt(26) + 1; j++) {
if (stringCharArray[j] == 'z') {
stringCharArray[j] = 'a';
} else {
stringCharArray[j] = (char) (((int) (stringCharArray[j])) + 1);
}
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");

}




Approach # 2 using hashmap is



public static void main(String args) {
HashMap hashMap = new HashMap();
hashMap.put('a', 'b');
hashMap.put('b', 'c');
hashMap.put('c', 'd');
hashMap.put('d', 'e');
hashMap.put('e', 'f');
hashMap.put('f', 'g');
hashMap.put('g', 'h');
hashMap.put('h', 'i');
hashMap.put('i', 'j');
hashMap.put('j', 'k');
hashMap.put('k', 'l');
hashMap.put('l', 'm');
hashMap.put('m', 'n');
hashMap.put('n', 'o');
hashMap.put('o', 'p');
hashMap.put('p', 'q');
hashMap.put('q', 'r');
hashMap.put('r', 's');
hashMap.put('s', 't');
hashMap.put('t', 'u');
hashMap.put('u', 'v');
hashMap.put('v', 'w');
hashMap.put('w', 'x');
hashMap.put('x', 'y');
hashMap.put('y', 'z');
hashMap.put('z', 'a');
Long startTime = System.currentTimeMillis();
String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (Integer i = 0; i <100000000; i++) {
{
for (Integer j = 0; j < random.nextInt(26) + 1; j++) {
stringCharArray[j] = (char) hashMap.get(stringCharArray[j]);
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");
}




Approach #3



#include <stdio.h>
#include <time.h>
#include <zconf.h>
#include <stdlib.h>

int main() {
long start = clock();
char name = "abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i <100000000; i++) {
{
for (int j = 0; j < rand() % 25; j++) {
if (name[j] == 'z') {
name[j] = 'a';
} else {
name[j] = (char) (((int) (name[j])) + 1);
}
}
}
}
long stop = clock();
printf("time taken = %ld sec n",( stop-start)/1000);
}




The runtime of approach 1 is ~ 8150 m, approach 2 is ~ 9700 ms and approach 3 is ~ 5400 ms. I am using MacBook Pro 2.3 GHz Intel Core i5 with 8 GB 2133 MHz LPDDR3.





How do I reduce this runtime, I used stream.parallelize() in java for apprach#2 to make the changes but the runtime was increasing. What am I doing wrong?



The goal is to get the runtime less than approach #3. Is there a way in C to parallelize this and solve in lesser time using pthreads?







java c multithreading






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 3 at 9:12









Jarod42

119k12104189




119k12104189










asked Jan 3 at 8:29









Amarjit SinghAmarjit Singh

1,0072728




1,0072728








  • 1





    Did you check how much time the programme spent in random functions ? Note also that all approaches are O(1)

    – Damien
    Jan 3 at 8:38








  • 4





    A better approach than the HashMap approach (which necessarily requires boxing) is to populate an array (or even a String): "bcde...xyza".charAt(name[0] - 'a').

    – Andy Turner
    Jan 3 at 8:43








  • 1





    for (int j = 0; j < random.nextInt(26) + 1; j++) will call nextint upon each iteation. I don't think you want that, so have a line before the for loop, e.g. int n=random.nextInt(26)+1; and then for (int j = 0; j < n; j++) This also optimizes the code because it eliminates unnecessary function calls.

    – Paul Ogilvie
    Jan 3 at 9:09








  • 1





    Notice than for (int j = 0; j < rand() % 25; j++) would call rand() at each iteration, you probably want: const int end = rand() % 25; for (int j = 0; j != end; j++).

    – Jarod42
    Jan 3 at 9:10






  • 1





    Instead of using stringCharArray[j], use a pointer variable. This optimizes the code by eliminating indexing instructions.

    – Paul Ogilvie
    Jan 3 at 9:12














  • 1





    Did you check how much time the programme spent in random functions ? Note also that all approaches are O(1)

    – Damien
    Jan 3 at 8:38








  • 4





    A better approach than the HashMap approach (which necessarily requires boxing) is to populate an array (or even a String): "bcde...xyza".charAt(name[0] - 'a').

    – Andy Turner
    Jan 3 at 8:43








  • 1





    for (int j = 0; j < random.nextInt(26) + 1; j++) will call nextint upon each iteation. I don't think you want that, so have a line before the for loop, e.g. int n=random.nextInt(26)+1; and then for (int j = 0; j < n; j++) This also optimizes the code because it eliminates unnecessary function calls.

    – Paul Ogilvie
    Jan 3 at 9:09








  • 1





    Notice than for (int j = 0; j < rand() % 25; j++) would call rand() at each iteration, you probably want: const int end = rand() % 25; for (int j = 0; j != end; j++).

    – Jarod42
    Jan 3 at 9:10






  • 1





    Instead of using stringCharArray[j], use a pointer variable. This optimizes the code by eliminating indexing instructions.

    – Paul Ogilvie
    Jan 3 at 9:12








1




1





Did you check how much time the programme spent in random functions ? Note also that all approaches are O(1)

– Damien
Jan 3 at 8:38







Did you check how much time the programme spent in random functions ? Note also that all approaches are O(1)

– Damien
Jan 3 at 8:38






4




4





A better approach than the HashMap approach (which necessarily requires boxing) is to populate an array (or even a String): "bcde...xyza".charAt(name[0] - 'a').

– Andy Turner
Jan 3 at 8:43







A better approach than the HashMap approach (which necessarily requires boxing) is to populate an array (or even a String): "bcde...xyza".charAt(name[0] - 'a').

– Andy Turner
Jan 3 at 8:43






1




1





for (int j = 0; j < random.nextInt(26) + 1; j++) will call nextint upon each iteation. I don't think you want that, so have a line before the for loop, e.g. int n=random.nextInt(26)+1; and then for (int j = 0; j < n; j++) This also optimizes the code because it eliminates unnecessary function calls.

– Paul Ogilvie
Jan 3 at 9:09







for (int j = 0; j < random.nextInt(26) + 1; j++) will call nextint upon each iteation. I don't think you want that, so have a line before the for loop, e.g. int n=random.nextInt(26)+1; and then for (int j = 0; j < n; j++) This also optimizes the code because it eliminates unnecessary function calls.

– Paul Ogilvie
Jan 3 at 9:09






1




1





Notice than for (int j = 0; j < rand() % 25; j++) would call rand() at each iteration, you probably want: const int end = rand() % 25; for (int j = 0; j != end; j++).

– Jarod42
Jan 3 at 9:10





Notice than for (int j = 0; j < rand() % 25; j++) would call rand() at each iteration, you probably want: const int end = rand() % 25; for (int j = 0; j != end; j++).

– Jarod42
Jan 3 at 9:10




1




1





Instead of using stringCharArray[j], use a pointer variable. This optimizes the code by eliminating indexing instructions.

– Paul Ogilvie
Jan 3 at 9:12





Instead of using stringCharArray[j], use a pointer variable. This optimizes the code by eliminating indexing instructions.

– Paul Ogilvie
Jan 3 at 9:12












2 Answers
2






active

oldest

votes


















1














Here's a version that is 8x faster on my PC than your C example.



#include <immintrin.h>

int main()
{
const __m256i compareConstant = _mm256_setr_epi8( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 );

long start = clock();
char name[ 32 ] = "abcdefghijklmnopqrstuvwxyz";
// __m256i is a C name for AVX register.
// AVX registers are 32 bytes in size, so your 26 bytes string fits in just a single one.
// The following line loads your string from memory to that register.
__m256i n = _mm256_loadu_si256( ( const __m256i* )name );
for( int i = 0; i < 100000000; i++ )
{
// Increment the letters, all 32 of them.
// `_mm256_set1_epi8` creates a value with all 32 bytes set to the same value.
// `_mm256_add_epi8` adds one set of 32 signed bytes to another set of 32 signed bytes.
// It's not a loop i.e. it's very fast, CPUs can actually run 2-3 such instructions per cycle.
__m256i n2 = _mm256_add_epi8( n, _mm256_set1_epi8( 1 ) );

// Wrap any > 'z' letters back to 'a'
// _mm256_cmpgt_epi8 compares one set of bytes to another set, for `>`.
// When it's `>` the result byte is set to 0xFF, when it's `<=` the result byte is 0.
// _mm256_blendv_epi8 combines bytes from 2 registers based on the bytes from the third one.
// In this case, the third one is the result of the comparison.
n2 = _mm256_blendv_epi8( n2, _mm256_set1_epi8( 'a' ), _mm256_cmpgt_epi8( n2, _mm256_set1_epi8( 'z' ) ) );

// Combine incremented ones with old, using random number of first characters
const int r = rand() % 25;
// This sets all 32 bytes in rv to the random number r
__m256i rv = _mm256_broadcastb_epi8( _mm_cvtsi32_si128( r ) );
// Compares all bytes in rv with the constant value [0, 1, 2, 3, ...]
// For bytes where r>cc, blendv_epi8 will select a byte from n2.
// For bytes where r<=cc, n will not change because blendv_epi8 will select an old value.
n = _mm256_blendv_epi8( n, n2, _mm256_cmpgt_epi8( rv, compareConstant ) );
}
long stop = clock();
printf( "time taken = %ld sec n", ( stop - start ) / 1000 );
}





share|improve this answer


























  • Can you please explain what you are doing and where these instructions come from (compiler, library,...)?

    – Paul Ogilvie
    Jan 3 at 9:17













  • @PaulOgilvie Re-implemented OP's code with AVX2 SIMD. The instructions are built-in in CPUs made after 2013, and are supported by all modern C and C++ compilers. As you see, performance difference is huge.

    – Soonts
    Jan 3 at 9:22











  • Thanks a lot for this code. I think I need a guide to understand this. U seem to be very well versed coder in C.

    – Amarjit Singh
    Jan 3 at 9:23






  • 1





    Notice that you call rand() only once by loop which should also help a lot :-)

    – Jarod42
    Jan 3 at 9:28






  • 1





    @AmarjitSingh Added more comments, hope it's simpler to understand now.

    – Soonts
    Jan 3 at 9:56



















1














Define a character array like



char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};


And you can get next either by directly accessing same index or by difference in ascii of characters



                stringCharArray[j] = nextChar[j];//by index
stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii


Complete code is below



    Long startTime = System.currentTimeMillis();

char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};
String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (int i = 0; i <100000000; i++) {
{
for (int j = 0; j < random.nextInt(27); j++) {
stringCharArray[j] = nextChar[j];//by index
//stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");





share|improve this answer


























  • Thanks a lot. I used this in C. The time reduced from ~5400 to ~5350. Definitely, there is some improvement.

    – Amarjit Singh
    Jan 3 at 9:01











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54018711%2freduce-runtime-for-manipulating-string-multiple-times-using-hashmap-other-meth%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














Here's a version that is 8x faster on my PC than your C example.



#include <immintrin.h>

int main()
{
const __m256i compareConstant = _mm256_setr_epi8( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 );

long start = clock();
char name[ 32 ] = "abcdefghijklmnopqrstuvwxyz";
// __m256i is a C name for AVX register.
// AVX registers are 32 bytes in size, so your 26 bytes string fits in just a single one.
// The following line loads your string from memory to that register.
__m256i n = _mm256_loadu_si256( ( const __m256i* )name );
for( int i = 0; i < 100000000; i++ )
{
// Increment the letters, all 32 of them.
// `_mm256_set1_epi8` creates a value with all 32 bytes set to the same value.
// `_mm256_add_epi8` adds one set of 32 signed bytes to another set of 32 signed bytes.
// It's not a loop i.e. it's very fast, CPUs can actually run 2-3 such instructions per cycle.
__m256i n2 = _mm256_add_epi8( n, _mm256_set1_epi8( 1 ) );

// Wrap any > 'z' letters back to 'a'
// _mm256_cmpgt_epi8 compares one set of bytes to another set, for `>`.
// When it's `>` the result byte is set to 0xFF, when it's `<=` the result byte is 0.
// _mm256_blendv_epi8 combines bytes from 2 registers based on the bytes from the third one.
// In this case, the third one is the result of the comparison.
n2 = _mm256_blendv_epi8( n2, _mm256_set1_epi8( 'a' ), _mm256_cmpgt_epi8( n2, _mm256_set1_epi8( 'z' ) ) );

// Combine incremented ones with old, using random number of first characters
const int r = rand() % 25;
// This sets all 32 bytes in rv to the random number r
__m256i rv = _mm256_broadcastb_epi8( _mm_cvtsi32_si128( r ) );
// Compares all bytes in rv with the constant value [0, 1, 2, 3, ...]
// For bytes where r>cc, blendv_epi8 will select a byte from n2.
// For bytes where r<=cc, n will not change because blendv_epi8 will select an old value.
n = _mm256_blendv_epi8( n, n2, _mm256_cmpgt_epi8( rv, compareConstant ) );
}
long stop = clock();
printf( "time taken = %ld sec n", ( stop - start ) / 1000 );
}





share|improve this answer


























  • Can you please explain what you are doing and where these instructions come from (compiler, library,...)?

    – Paul Ogilvie
    Jan 3 at 9:17













  • @PaulOgilvie Re-implemented OP's code with AVX2 SIMD. The instructions are built-in in CPUs made after 2013, and are supported by all modern C and C++ compilers. As you see, performance difference is huge.

    – Soonts
    Jan 3 at 9:22











  • Thanks a lot for this code. I think I need a guide to understand this. U seem to be very well versed coder in C.

    – Amarjit Singh
    Jan 3 at 9:23






  • 1





    Notice that you call rand() only once by loop which should also help a lot :-)

    – Jarod42
    Jan 3 at 9:28






  • 1





    @AmarjitSingh Added more comments, hope it's simpler to understand now.

    – Soonts
    Jan 3 at 9:56
















1














Here's a version that is 8x faster on my PC than your C example.



#include <immintrin.h>

int main()
{
const __m256i compareConstant = _mm256_setr_epi8( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 );

long start = clock();
char name[ 32 ] = "abcdefghijklmnopqrstuvwxyz";
// __m256i is a C name for AVX register.
// AVX registers are 32 bytes in size, so your 26 bytes string fits in just a single one.
// The following line loads your string from memory to that register.
__m256i n = _mm256_loadu_si256( ( const __m256i* )name );
for( int i = 0; i < 100000000; i++ )
{
// Increment the letters, all 32 of them.
// `_mm256_set1_epi8` creates a value with all 32 bytes set to the same value.
// `_mm256_add_epi8` adds one set of 32 signed bytes to another set of 32 signed bytes.
// It's not a loop i.e. it's very fast, CPUs can actually run 2-3 such instructions per cycle.
__m256i n2 = _mm256_add_epi8( n, _mm256_set1_epi8( 1 ) );

// Wrap any > 'z' letters back to 'a'
// _mm256_cmpgt_epi8 compares one set of bytes to another set, for `>`.
// When it's `>` the result byte is set to 0xFF, when it's `<=` the result byte is 0.
// _mm256_blendv_epi8 combines bytes from 2 registers based on the bytes from the third one.
// In this case, the third one is the result of the comparison.
n2 = _mm256_blendv_epi8( n2, _mm256_set1_epi8( 'a' ), _mm256_cmpgt_epi8( n2, _mm256_set1_epi8( 'z' ) ) );

// Combine incremented ones with old, using random number of first characters
const int r = rand() % 25;
// This sets all 32 bytes in rv to the random number r
__m256i rv = _mm256_broadcastb_epi8( _mm_cvtsi32_si128( r ) );
// Compares all bytes in rv with the constant value [0, 1, 2, 3, ...]
// For bytes where r>cc, blendv_epi8 will select a byte from n2.
// For bytes where r<=cc, n will not change because blendv_epi8 will select an old value.
n = _mm256_blendv_epi8( n, n2, _mm256_cmpgt_epi8( rv, compareConstant ) );
}
long stop = clock();
printf( "time taken = %ld sec n", ( stop - start ) / 1000 );
}





share|improve this answer


























  • Can you please explain what you are doing and where these instructions come from (compiler, library,...)?

    – Paul Ogilvie
    Jan 3 at 9:17













  • @PaulOgilvie Re-implemented OP's code with AVX2 SIMD. The instructions are built-in in CPUs made after 2013, and are supported by all modern C and C++ compilers. As you see, performance difference is huge.

    – Soonts
    Jan 3 at 9:22











  • Thanks a lot for this code. I think I need a guide to understand this. U seem to be very well versed coder in C.

    – Amarjit Singh
    Jan 3 at 9:23






  • 1





    Notice that you call rand() only once by loop which should also help a lot :-)

    – Jarod42
    Jan 3 at 9:28






  • 1





    @AmarjitSingh Added more comments, hope it's simpler to understand now.

    – Soonts
    Jan 3 at 9:56














1












1








1







Here's a version that is 8x faster on my PC than your C example.



#include <immintrin.h>

int main()
{
const __m256i compareConstant = _mm256_setr_epi8( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 );

long start = clock();
char name[ 32 ] = "abcdefghijklmnopqrstuvwxyz";
// __m256i is a C name for AVX register.
// AVX registers are 32 bytes in size, so your 26 bytes string fits in just a single one.
// The following line loads your string from memory to that register.
__m256i n = _mm256_loadu_si256( ( const __m256i* )name );
for( int i = 0; i < 100000000; i++ )
{
// Increment the letters, all 32 of them.
// `_mm256_set1_epi8` creates a value with all 32 bytes set to the same value.
// `_mm256_add_epi8` adds one set of 32 signed bytes to another set of 32 signed bytes.
// It's not a loop i.e. it's very fast, CPUs can actually run 2-3 such instructions per cycle.
__m256i n2 = _mm256_add_epi8( n, _mm256_set1_epi8( 1 ) );

// Wrap any > 'z' letters back to 'a'
// _mm256_cmpgt_epi8 compares one set of bytes to another set, for `>`.
// When it's `>` the result byte is set to 0xFF, when it's `<=` the result byte is 0.
// _mm256_blendv_epi8 combines bytes from 2 registers based on the bytes from the third one.
// In this case, the third one is the result of the comparison.
n2 = _mm256_blendv_epi8( n2, _mm256_set1_epi8( 'a' ), _mm256_cmpgt_epi8( n2, _mm256_set1_epi8( 'z' ) ) );

// Combine incremented ones with old, using random number of first characters
const int r = rand() % 25;
// This sets all 32 bytes in rv to the random number r
__m256i rv = _mm256_broadcastb_epi8( _mm_cvtsi32_si128( r ) );
// Compares all bytes in rv with the constant value [0, 1, 2, 3, ...]
// For bytes where r>cc, blendv_epi8 will select a byte from n2.
// For bytes where r<=cc, n will not change because blendv_epi8 will select an old value.
n = _mm256_blendv_epi8( n, n2, _mm256_cmpgt_epi8( rv, compareConstant ) );
}
long stop = clock();
printf( "time taken = %ld sec n", ( stop - start ) / 1000 );
}





share|improve this answer















Here's a version that is 8x faster on my PC than your C example.



#include <immintrin.h>

int main()
{
const __m256i compareConstant = _mm256_setr_epi8( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 );

long start = clock();
char name[ 32 ] = "abcdefghijklmnopqrstuvwxyz";
// __m256i is a C name for AVX register.
// AVX registers are 32 bytes in size, so your 26 bytes string fits in just a single one.
// The following line loads your string from memory to that register.
__m256i n = _mm256_loadu_si256( ( const __m256i* )name );
for( int i = 0; i < 100000000; i++ )
{
// Increment the letters, all 32 of them.
// `_mm256_set1_epi8` creates a value with all 32 bytes set to the same value.
// `_mm256_add_epi8` adds one set of 32 signed bytes to another set of 32 signed bytes.
// It's not a loop i.e. it's very fast, CPUs can actually run 2-3 such instructions per cycle.
__m256i n2 = _mm256_add_epi8( n, _mm256_set1_epi8( 1 ) );

// Wrap any > 'z' letters back to 'a'
// _mm256_cmpgt_epi8 compares one set of bytes to another set, for `>`.
// When it's `>` the result byte is set to 0xFF, when it's `<=` the result byte is 0.
// _mm256_blendv_epi8 combines bytes from 2 registers based on the bytes from the third one.
// In this case, the third one is the result of the comparison.
n2 = _mm256_blendv_epi8( n2, _mm256_set1_epi8( 'a' ), _mm256_cmpgt_epi8( n2, _mm256_set1_epi8( 'z' ) ) );

// Combine incremented ones with old, using random number of first characters
const int r = rand() % 25;
// This sets all 32 bytes in rv to the random number r
__m256i rv = _mm256_broadcastb_epi8( _mm_cvtsi32_si128( r ) );
// Compares all bytes in rv with the constant value [0, 1, 2, 3, ...]
// For bytes where r>cc, blendv_epi8 will select a byte from n2.
// For bytes where r<=cc, n will not change because blendv_epi8 will select an old value.
n = _mm256_blendv_epi8( n, n2, _mm256_cmpgt_epi8( rv, compareConstant ) );
}
long stop = clock();
printf( "time taken = %ld sec n", ( stop - start ) / 1000 );
}






share|improve this answer














share|improve this answer



share|improve this answer








edited Jan 3 at 9:48

























answered Jan 3 at 9:16









SoontsSoonts

9,10083576




9,10083576













  • Can you please explain what you are doing and where these instructions come from (compiler, library,...)?

    – Paul Ogilvie
    Jan 3 at 9:17













  • @PaulOgilvie Re-implemented OP's code with AVX2 SIMD. The instructions are built-in in CPUs made after 2013, and are supported by all modern C and C++ compilers. As you see, performance difference is huge.

    – Soonts
    Jan 3 at 9:22











  • Thanks a lot for this code. I think I need a guide to understand this. U seem to be very well versed coder in C.

    – Amarjit Singh
    Jan 3 at 9:23






  • 1





    Notice that you call rand() only once by loop which should also help a lot :-)

    – Jarod42
    Jan 3 at 9:28






  • 1





    @AmarjitSingh Added more comments, hope it's simpler to understand now.

    – Soonts
    Jan 3 at 9:56



















  • Can you please explain what you are doing and where these instructions come from (compiler, library,...)?

    – Paul Ogilvie
    Jan 3 at 9:17













  • @PaulOgilvie Re-implemented OP's code with AVX2 SIMD. The instructions are built-in in CPUs made after 2013, and are supported by all modern C and C++ compilers. As you see, performance difference is huge.

    – Soonts
    Jan 3 at 9:22











  • Thanks a lot for this code. I think I need a guide to understand this. U seem to be very well versed coder in C.

    – Amarjit Singh
    Jan 3 at 9:23






  • 1





    Notice that you call rand() only once by loop which should also help a lot :-)

    – Jarod42
    Jan 3 at 9:28






  • 1





    @AmarjitSingh Added more comments, hope it's simpler to understand now.

    – Soonts
    Jan 3 at 9:56

















Can you please explain what you are doing and where these instructions come from (compiler, library,...)?

– Paul Ogilvie
Jan 3 at 9:17







Can you please explain what you are doing and where these instructions come from (compiler, library,...)?

– Paul Ogilvie
Jan 3 at 9:17















@PaulOgilvie Re-implemented OP's code with AVX2 SIMD. The instructions are built-in in CPUs made after 2013, and are supported by all modern C and C++ compilers. As you see, performance difference is huge.

– Soonts
Jan 3 at 9:22





@PaulOgilvie Re-implemented OP's code with AVX2 SIMD. The instructions are built-in in CPUs made after 2013, and are supported by all modern C and C++ compilers. As you see, performance difference is huge.

– Soonts
Jan 3 at 9:22













Thanks a lot for this code. I think I need a guide to understand this. U seem to be very well versed coder in C.

– Amarjit Singh
Jan 3 at 9:23





Thanks a lot for this code. I think I need a guide to understand this. U seem to be very well versed coder in C.

– Amarjit Singh
Jan 3 at 9:23




1




1





Notice that you call rand() only once by loop which should also help a lot :-)

– Jarod42
Jan 3 at 9:28





Notice that you call rand() only once by loop which should also help a lot :-)

– Jarod42
Jan 3 at 9:28




1




1





@AmarjitSingh Added more comments, hope it's simpler to understand now.

– Soonts
Jan 3 at 9:56





@AmarjitSingh Added more comments, hope it's simpler to understand now.

– Soonts
Jan 3 at 9:56













1














Define a character array like



char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};


And you can get next either by directly accessing same index or by difference in ascii of characters



                stringCharArray[j] = nextChar[j];//by index
stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii


Complete code is below



    Long startTime = System.currentTimeMillis();

char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};
String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (int i = 0; i <100000000; i++) {
{
for (int j = 0; j < random.nextInt(27); j++) {
stringCharArray[j] = nextChar[j];//by index
//stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");





share|improve this answer


























  • Thanks a lot. I used this in C. The time reduced from ~5400 to ~5350. Definitely, there is some improvement.

    – Amarjit Singh
    Jan 3 at 9:01
















1














Define a character array like



char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};


And you can get next either by directly accessing same index or by difference in ascii of characters



                stringCharArray[j] = nextChar[j];//by index
stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii


Complete code is below



    Long startTime = System.currentTimeMillis();

char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};
String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (int i = 0; i <100000000; i++) {
{
for (int j = 0; j < random.nextInt(27); j++) {
stringCharArray[j] = nextChar[j];//by index
//stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");





share|improve this answer


























  • Thanks a lot. I used this in C. The time reduced from ~5400 to ~5350. Definitely, there is some improvement.

    – Amarjit Singh
    Jan 3 at 9:01














1












1








1







Define a character array like



char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};


And you can get next either by directly accessing same index or by difference in ascii of characters



                stringCharArray[j] = nextChar[j];//by index
stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii


Complete code is below



    Long startTime = System.currentTimeMillis();

char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};
String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (int i = 0; i <100000000; i++) {
{
for (int j = 0; j < random.nextInt(27); j++) {
stringCharArray[j] = nextChar[j];//by index
//stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");





share|improve this answer















Define a character array like



char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};


And you can get next either by directly accessing same index or by difference in ascii of characters



                stringCharArray[j] = nextChar[j];//by index
stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii


Complete code is below



    Long startTime = System.currentTimeMillis();

char nextChar = {'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','a'};
String name = "abcdefghijklmnopqrstuvwxyz";
char stringCharArray = name.toCharArray();
Random random = new Random();
for (int i = 0; i <100000000; i++) {
{
for (int j = 0; j < random.nextInt(27); j++) {
stringCharArray[j] = nextChar[j];//by index
//stringCharArray[j] = nextChar[stringCharArray[j]-'a'];// using ascii
}
}
}
Long endtime = System.currentTimeMillis();
System.out.println(endtime-startTime+" ms");






share|improve this answer














share|improve this answer



share|improve this answer








edited Jan 3 at 9:00

























answered Jan 3 at 8:53









NGVNGV

2,18122040




2,18122040













  • Thanks a lot. I used this in C. The time reduced from ~5400 to ~5350. Definitely, there is some improvement.

    – Amarjit Singh
    Jan 3 at 9:01



















  • Thanks a lot. I used this in C. The time reduced from ~5400 to ~5350. Definitely, there is some improvement.

    – Amarjit Singh
    Jan 3 at 9:01

















Thanks a lot. I used this in C. The time reduced from ~5400 to ~5350. Definitely, there is some improvement.

– Amarjit Singh
Jan 3 at 9:01





Thanks a lot. I used this in C. The time reduced from ~5400 to ~5350. Definitely, there is some improvement.

– Amarjit Singh
Jan 3 at 9:01


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54018711%2freduce-runtime-for-manipulating-string-multiple-times-using-hashmap-other-meth%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







p,cDMLDQxAJ9ItiqeUvN8eePVD73XrSthu93p 7mboxDl4pjY TE6t1qoI
MeG lHMB3F XuFp0qCRdG2WsFB7,dJ22X68QY4eHRThcuwfYZ0 r ihsO7SN AL35SxZVKUBJHx

Popular posts from this blog

Monofisismo

Angular Downloading a file using contenturl with Basic Authentication

Olmecas