Bulk hash insert with multiple Hash Key value using StackExchange.Redis - redis

i am using Redis server - 4.0.9 , StackExchange.Redis (2.2.4).
i am trying to insert my csv data into Redis hash CSV contain ~10K Record and file size is 2 mb.
its take around 17 second to push 10k record.
Here is all my Test Result -
simple Key, value pair Process Time for ~100k Record -> 8 Second
using StringSetAsync.
Hash Key with 2 pair Process Time sec ~100k Record -> 14 Second
HashSetAsync.
Hash Key CSV Process Time sec ~10K Record -> 17 Second
I am using pipelining for process this record .
public class Redis_Test
{
private static ConfigurationOptions _configurationOptions;
public Redis_Test(string argStrEndPoints)
{
_configurationOptions = new ConfigurationOptions
{
EndPoints = { argStrEndPoints },
AbortOnConnectFail = false,
KeepAlive = 60
};
}
public Redis_Test(string argStrEndPoints, string argStrPassword)
{
_configurationOptions = new ConfigurationOptions
{
EndPoints = { argStrEndPoints },
Password = argStrPassword,
AbortOnConnectFail = false,
KeepAlive = 60
};
}
private static IDatabase RedisCache
{
get
{
return Connection.GetDatabase();
}
}
private static readonly Lazy<ConnectionMultiplexer> LazyConnection
= new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(_configurationOptions));
public static ConnectionMultiplexer Connection
{
get
{
return LazyConnection.Value;
}
}
public void testpipe()
{
List<Task> redisTask = new List<Task>();
int batch = 10000;
for (int i = 0; i <= 100000; i++)
{
Task<bool> addAsyncTask = RedisCache.StringSetAsync("testKey:" + i.ToString(), "Value:" + i.ToString());
redisTask.Add(addAsyncTask);
if (i == batch)
{
Task[] TaskArray = redisTask.ToArray();
Task.WaitAll(TaskArray);
redisTask.Clear();
batch = batch + 10000;
}
}
}
public void testpipehash()
{
List<Task> redisTask = new List<Task>();
int batch = 10000;
for (int i = 0; i <= 100000; i++)
{
HashEntry[] ab = new HashEntry[] { new HashEntry("hash1"+ i.ToString(), "hashvalue1"+i.ToString()),
new HashEntry("hash2" + i.ToString(), "hashvalue2" + i.ToString()) };
Task addAsyncTask = RedisCache.HashSetAsync("testhashKey:" + i.ToString(), ab);
redisTask.Add(addAsyncTask);
if (i == batch)
{
Task[] TaskArray = redisTask.ToArray();
Task.WaitAll(TaskArray);
redisTask.Clear();
batch = batch + 10000;
}
}
}
public int testpipehashCSV(DataTable dataTable)
{
List<Task> redisTask = new List<Task>();
int batch = 1000;
var watch = System.Diagnostics.Stopwatch.StartNew();
int i = 0;
foreach (DataRow dr in dataTable.Rows)
{
// converting row to dic
watch.Stop();
Dictionary<string, string> test = new Dictionary<string, string>();
test = dr.Table.Columns
.Cast<DataColumn>()
.ToDictionary(c => c.ColumnName, c => dr[c].ToString());
//converting dic to hashentry[]
var hashEntries = test.Select(
Pair => new HashEntry(Pair.Key, Pair.Value)).ToArray();
watch.Start();
Task addAsyncTask = RedisCache.HashSetAsync("testhashKeyCSV:" + i.ToString(), hashEntries);
redisTask.Add(addAsyncTask);
if (i == batch)
{
Task[] TaskArray = redisTask.ToArray();
Task.WaitAll(TaskArray);
redisTask.Clear();
batch = batch + 1000;
}
i++;
}
watch.Stop();
TimeSpan ts = watch.Elapsed;
int totaltimeinsec = ts.Seconds;
return totaltimeinsec;
}
}
Please any suggestion where i can improve my CSV file data load into Redis Hash.
Thanks.

Related

Automating Import of large csv files(~3gb) with C#

I am a bit new to this but my goal is to import the data from a csv file into a sql table and include additional values for each row being the file name and date. I was able to accomplish this using entity frame work and iterating through each row of the file but with the size of the files it will take too long too actually complete.
I am looking for a method to accomplish this import faster. I was looking into potentially using csvhelper with sqlbulkcopy to accomplish this but was not sure if there was a way to pass in the additional values needed for each row.
public void Process(string filePath)
{
InputFilePath = filePath;
DateTime fileDate = DateTime.Today;
string[] fPath = Directory.GetFiles(InputFilePath);
foreach (var file in fPath)
{
string fileName = Path.GetFileName(file);
char[] delimiter = new char[] { '\t' };
try
{
using (var db = new DatabaseName())
{
using (var reader = new StreamReader(file))
{
string line;
int count = 0;
int sCount = 0;
reader.ReadLine();
reader.ReadLine();
while ((line = reader.ReadLine()) != null)
{
count++;
string[] row = line.Split(delimiter);
var rowload = new ImportDestinationTable()
{
ImportCol0 = row[0],
ImportCol1 = row[1],
ImportCol2 = TryParseNullable(row[2]),
ImportCol3 = row[3],
ImportCol4 = row[4],
ImportCol5 = row[5],
IMPORT_FILE_NM = fileName,
IMPORT_DT = fileDate
};
db.ImportDestinationTable.Add(rowload);
if (count > 100)
{
db.SaveChanges();
count = 0;
}
}
db.SaveChanges();
//ReadLine();
}
}
}
static int? TryParseNullable(string val)
{
int outValue;
return int.TryParse(val, out outValue) ? (int?)outValue : null;
}
}

Is it possible to return an array of dictionaries?

I wrote a function that will return a single sql record as a dictionary. Is it possible to return an array of dictionaries so I can return multiple records in this way?
public static async Task<Dictionary<string, string>> SQLMultiRecordToDictionary(string TableName, string SearchField, string SearchValue)
{
Dictionary<string, string> QueryResult = new Dictionary<string, string>();
// is TableName sane
if (!IsTextSane(TableName)) { return QueryResult; }
//
await using (var connection = new SqliteConnection("Data Source=" + dbFullPathName))
{
connection.Open();
SqliteCommand sqlcmd = connection.CreateCommand();
sqlcmd.CommandText = "SELECT * FROM " + TableName + " WHERE " + SearchField + "=#SearchValue";
sqlcmd.Parameters.AddWithValue("#SearchValue", SearchValue);
SqliteDataReader sqlreader = sqlcmd.ExecuteReader();
// generate dictionary keys with blank values
// this prevents key not existing issues when no record is returned
// i prefer no/blank values in the keys when no record returned for this project
for (int i = 0; i < sqlreader.FieldCount; i++)
{
QueryResult.Add(sqlreader.GetName(i), ""); // blank value
}
// add the values to the keys
while (sqlreader.Read())
{
for (int i = 0; i <= sqlreader.FieldCount - 1; i++)
{
QueryResult[sqlreader.GetName(i)] = sqlreader.GetString(i);
}
}
return QueryResult;
}
}
The working end result thanks to Tisa:
public static async Task<List<Dictionary<string, string>>> SQLMultiRecordToDictionaryList(string TableName, string SearchField, string SearchValue)
{
List<Dictionary<string, string>> QueryResult = new List<Dictionary<string, string>>();
Dictionary<string, string> SQLRecord = new Dictionary<string, string>();
//
// is TableName sane, if not return nothing
if (!IsTextSane(TableName)) { return QueryResult; }
//
await using (var connection = new SqliteConnection("Data Source=" + dbFullPathName))
{
connection.Open();
SqliteCommand sqlcmd = connection.CreateCommand();
sqlcmd.CommandText = "SELECT * FROM " + TableName + " WHERE " + SearchField + "=#SearchValue";
sqlcmd.Parameters.AddWithValue("#SearchValue", SearchValue);
SqliteDataReader sqlreader = sqlcmd.ExecuteReader();
// generate dictionary keys with blank values if no rows
// this prevents key not existing issues when no record is returned
// i prefer no/blank values in the keys when no record returned for this project
if (!sqlreader.HasRows)
{
for (int i = 0; i < sqlreader.FieldCount; i++)
{
SQLRecord.Add(sqlreader.GetName(i), ""); // blank value
}
QueryResult.Add(SQLRecord);
}
//
// add the values to the keys if there are rows (this doesn't run if no rows returned)
while (sqlreader.Read())
{
SQLRecord = new Dictionary<string, string>();
for (int i = 0; i <= sqlreader.FieldCount - 1; i++)
{
SQLRecord.Add(sqlreader.GetName(i), sqlreader.GetString(i));
}
QueryResult.Add(SQLRecord);
}
return QueryResult;
}
}

Hearing Clipping after using NAudio MixingSampleProvider

I'm hoping someone can help me. I am using NAudio and am recording from multiple wsapiloopbackcapture devices and mixing them together. None of this can be written to disk, so I am doing it all in memory and am ultimately loading the data into a concurrent dictionary for another process to use that I will develop later. All this works except that whenever I run my thread to load my chunk data into my dictionary, when I play the audio back I hear a single clip sound and it coincides with how often I run the thread. If I set it to run every 20 seconds then I hear a clip sound at the 20 second mark when I listen to the audio. I need to get rid of this clipping and I can't figure out what is causing it.
Here are the basic steps
private static WasapiLoopbackCapture Wavein = null;
private static WasapiLoopbackCapture Wavein2 = null;
private static WaveFormat pcm8k16bitSt = new WaveFormat(8000, 16, 2);
private static WaveFormat pcm8k16bitMo = new WaveFormat(8000, 16, 1);
private static WaveFormat Bit16;
private static byte[] DataHeader = System.Text.Encoding.UTF8.GetBytes("data");
private static List<byte> SBL = new List<byte>();
private static List<byte> SBL2 = new List<byte>();
private static byte[] chunkdata;
private static byte[] chunkdata2;
internal static ConcurrentDictionary<DateTime, DataFrame> AllSpeakerBytes = new ConcurrentDictionary<DateTime, DataFrame>();
int SdeviceNumber = 0;
int SdeviceNumber2 = 0;
private static MMDevice deviceToRecord = null;
private static MMDevice deviceToRecord2 = null;
deviceToRecord = (new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active))[SdeviceNumber];
deviceToRecord2 = (new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active))[SdeviceNumber2];
RecordAllSpeakers();
private void RecordAllSpeakers()
{
if (deviceToRecord != null)
{
Wavein = new WasapiLoopbackCapture(deviceToRecord);
var silence = new SilenceProvider(Wavein.WaveFormat).ToSampleProvider();
var wo = new WaveOutEvent();
wo.DeviceNumber = SdeviceNumber;
wo.Init(silence);
wo.Play();
Wavein.DataAvailable += new EventHandler<NAudio.Wave.WaveInEventArgs>(SsourceStream_DataAvailable);
Wavein.StartRecording();
SRecFlag = true;
}
if (deviceToRecord2 != null)
{
Wavein2 = new WasapiLoopbackCapture(deviceToRecord2);
var silence = new SilenceProvider(Wavein2.WaveFormat).ToSampleProvider();
var wo = new WaveOutEvent();
wo.DeviceNumber = SdeviceNumber2;
wo.Init(silence);
wo.Play();
Wavein2.DataAvailable += new EventHandler<NAudio.Wave.WaveInEventArgs>(SsourceStream2_DataAvailable);
Wavein2.StartRecording();
SRecFlag2 = true;
}
}
private void SsourceStream_DataAvailable(object sender, NAudio.Wave.WaveInEventArgs e)
{
if (!SRecFlag) return;
if (Wavein.WaveFormat.BitsPerSample == 32)
{
#region NewStraightConvert
using (RawSourceWaveStream RS = new RawSourceWaveStream(e.Buffer, 0, e.BytesRecorded, WaveFormat.CreateIeeeFloatWaveFormat(Wavein.WaveFormat.SampleRate, Wavein.WaveFormat.Channels)))
using (Wave32To16Stream wav16 = new Wave32To16Stream(RS))
using (MemoryStream ms2 = new MemoryStream())
{
WaveFileWriter.WriteWavFileToStream(ms2, wav16);
ms2.Position = 0;
using (var reader = new WaveFileReader(ms2))
using (var conversionStream0 = new WaveFormatConversionStream(pcm8k16bitSt, reader))
using (var conversionStream1 = new WaveFormatConversionStream(pcm8k16bitMo, conversionStream0))
//using (var conversionStream2 = new WaveFormatConversionStream(muLaw8k8bit, conversionStream1))
{
byte[] SendingBytes;
using (MemoryStream ms3 = new MemoryStream())
{
using (RawSourceWaveStream cc = new RawSourceWaveStream(conversionStream1, pcm8k16bitMo))
{
cc.Position = 0;
cc.CopyTo(ms3);
SendingBytes = ms3.ToArray();
}
}
if (SendingBytes.Length > 0)
{
//SslTcpClient.VociDataToSendST2.AddRange(SendingBytes);
SBL.AddRange(SendingBytes);
}
}
}
#endregion
return;
}
else
{
byte[] outtovoci;
Bit16 = new WaveFormat(Wavein.WaveFormat.SampleRate, 16, Wavein.WaveFormat.Channels);
outtovoci = new byte[e.BytesRecorded];
Array.Copy(e.Buffer, 0, outtovoci, 0, e.BytesRecorded);
using (MemoryStream TESTWaveMS = new MemoryStream())
{
using (MemoryStream TESTWaveMS2 = new MemoryStream())
{
using (WaveFileWriter TESTwaveWriter = new WaveFileWriter(TESTWaveMS, Bit16))
{
TESTwaveWriter.Write(outtovoci, 0, outtovoci.Length);
TESTwaveWriter.Flush();
byte[] tbytes = TESTWaveMS.ToArray();
using (MemoryStream tstream = new MemoryStream(tbytes))
{
using (var reader = new WaveFileReader(tstream))
using (var conversionStream0 = new WaveFormatConversionStream(pcm8k16bitSt, reader))
using (var conversionStream1 = new WaveFormatConversionStream(pcm8k16bitMo, conversionStream0))
//using (var conversionStream2 = new WaveFormatConversionStream(muLaw8k8bit, conversionStream1))
{
WaveFileWriter.WriteWavFileToStream(TESTWaveMS2, conversionStream1);
byte[] tbytes2 = TESTWaveMS2.ToArray();
int fPos = SearchBytes(tbytes2, DataHeader);
if (fPos > 0)
{
fPos = fPos + 8;
}
else
{
fPos = 0;
}
long SendingBytes = tbytes2.Length - fPos;
byte[] WBack = new byte[SendingBytes];
if (SendingBytes > 0)
{
Array.Copy(tbytes2, fPos, WBack, 0, SendingBytes);
//SslTcpClient.VociDataToSendST2.AddRange(WBack);
SBL.AddRange(WBack);
}
}
}
}
}
}
}
}
private void SsourceStream2_DataAvailable(object sender, NAudio.Wave.WaveInEventArgs e)
{
if (!SRecFlag2) return;
if (Wavein2.WaveFormat.BitsPerSample == 32)
{
#region NewStraightConvert
using (RawSourceWaveStream RS = new RawSourceWaveStream(e.Buffer, 0, e.BytesRecorded, WaveFormat.CreateIeeeFloatWaveFormat(Wavein2.WaveFormat.SampleRate, Wavein2.WaveFormat.Channels)))
using (Wave32To16Stream wav16 = new Wave32To16Stream(RS))
using (MemoryStream ms2 = new MemoryStream())
{
WaveFileWriter.WriteWavFileToStream(ms2, wav16);
ms2.Position = 0;
using (var reader = new WaveFileReader(ms2))
using (var conversionStream0 = new WaveFormatConversionStream(pcm8k16bitSt, reader))
using (var conversionStream1 = new WaveFormatConversionStream(pcm8k16bitMo, conversionStream0))
//using (var conversionStream2 = new WaveFormatConversionStream(muLaw8k8bit, conversionStream1))
{
byte[] SendingBytes;
using (MemoryStream ms3 = new MemoryStream())
{
using (RawSourceWaveStream cc = new RawSourceWaveStream(conversionStream1, pcm8k16bitMo))
{
cc.Position = 0;
cc.CopyTo(ms3);
SendingBytes = ms3.ToArray();
}
}
if (SendingBytes.Length > 0)
{
//SslTcpClient.VociDataToSendST2.AddRange(SendingBytes);
SBL2.AddRange(SendingBytes);
}
}
}
#endregion
return;
}
else
{
byte[] outtovoci;
Bit16 = new WaveFormat(Wavein2.WaveFormat.SampleRate, 16, Wavein2.WaveFormat.Channels);
outtovoci = new byte[e.BytesRecorded];
Array.Copy(e.Buffer, 0, outtovoci, 0, e.BytesRecorded);
using (MemoryStream TESTWaveMS = new MemoryStream())
{
using (MemoryStream TESTWaveMS2 = new MemoryStream())
{
using (WaveFileWriter TESTwaveWriter = new WaveFileWriter(TESTWaveMS, Bit16))
{
TESTwaveWriter.Write(outtovoci, 0, outtovoci.Length);
TESTwaveWriter.Flush();
byte[] tbytes = TESTWaveMS.ToArray();
using (MemoryStream tstream = new MemoryStream(tbytes))
{
using (var reader = new WaveFileReader(tstream))
using (var conversionStream0 = new WaveFormatConversionStream(pcm8k16bitSt, reader))
using (var conversionStream1 = new WaveFormatConversionStream(pcm8k16bitMo, conversionStream0))
//using (var conversionStream2 = new WaveFormatConversionStream(muLaw8k8bit, conversionStream1))
{
WaveFileWriter.WriteWavFileToStream(TESTWaveMS2, conversionStream1);
byte[] tbytes2 = TESTWaveMS2.ToArray();
int fPos = SearchBytes(tbytes2, DataHeader);
if (fPos > 0)
{
fPos = fPos + 8;
}
else
{
fPos = 0;
}
long SendingBytes = tbytes2.Length - fPos;
byte[] WBack = new byte[SendingBytes];
if (SendingBytes > 0)
{
Array.Copy(tbytes2, fPos, WBack, 0, SendingBytes);
//SslTcpClient.VociDataToSendST2.AddRange(WBack);
SBL2.AddRange(WBack);
}
}
}
}
}
}
}
}
private async void timer3_Tick(object sender, EventArgs e)
{
timer3.Enabled = false;
if (SRecFlag == true || SRecFlag2 == true)
{
await Task.Run(() => SyncSpeakers());
}
timer3.Interval = 20000;
timer3.Enabled = true;
}
private static void SyncSpeakers()
{
MemoryStream ms = new MemoryStream();
MemoryStream ms2 = new MemoryStream();
WaveFileReader reader = null;
WaveFileReader reader2 = null;
MixingSampleProvider mixer = null;
int lbc = SBL.Count();
int lbc2 = SBL2.Count();
int lowest = 0;
int[] array = new int[] { lbc, lbc2 };
lowest = array.Where(f => f > 0).Min();
if (deviceToRecord != null && SBL.Count > 0)
{
chunkdata = new byte[lowest];
Array.Copy(SBL.ToArray(), 0, chunkdata, 0, chunkdata.Length);
SwaveWriterS1 = new NAudio.Wave.WaveFileWriter(ms, pcm8k16bitMo);
SwaveWriterS1.Write(chunkdata, 0, chunkdata.Length);
SwaveWriterS1.Flush();
SBL.RemoveRange(0, lowest);
}
if (deviceToRecord2 != null && SBL2.Count > 0)
{
chunkdata2 = new byte[lowest];
Array.Copy(SBL2.ToArray(), 0, chunkdata2, 0, chunkdata2.Length);
SwaveWriterS2 = new NAudio.Wave.WaveFileWriter(ms2, pcm8k16bitMo);
SwaveWriterS2.Write(chunkdata2, 0, chunkdata2.Length);
SwaveWriterS2.Flush();
SBL2.RemoveRange(0, lowest);
}
int SWaves = 0;
if (Wavein != null && SRecFlag == true)
{
ms.Position = 0;
reader = new WaveFileReader(ms);
SWaves++;
}
if (Wavein2 != null && SRecFlag2 == true)
{
ms2.Position = 0;
reader2 = new WaveFileReader(ms2);
SWaves++;
}
if (SWaves == 1)
{
mixer = new MixingSampleProvider(new[] { reader.ToSampleProvider() });
}
else if (SWaves == 2)
{
mixer = new MixingSampleProvider(new[] { reader.ToSampleProvider(), reader2.ToSampleProvider() });
}
if (SWaves > 0)
{
using (MemoryStream lms = new MemoryStream())
{
WaveFileWriter.WriteWavFileToStream(lms, mixer.ToWaveProvider16());
byte[] SendingBytes;
using (MemoryStream ms35 = new MemoryStream())
{
using (RawSourceWaveStream cc = new RawSourceWaveStream(lms, pcm8k16bitMo))
{
cc.Position = 0;
cc.CopyTo(ms35);
SendingBytes = ms35.ToArray();
SwaveWriter.Write(SendingBytes, 0, SendingBytes.Length);
SwaveWriter.Flush();
byte[] lByte = Compress(SendingBytes);
DataFrame aFrame2 = new DataFrame();
aFrame2.bytes = new byte[lByte.Length];
aFrame2.bytesRecorded = SendingBytes.Length;
lByte.CopyTo(aFrame2.bytes, 0);
AllSpeakerBytes.TryAdd(DateTime.UtcNow, aFrame2);
lByte = null;
}
}
}
}
}
internal static byte[] Compress(byte[] data)
{
try
{
//return data;
using (MemoryStream output = new MemoryStream())
{
using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Optimal))
{
dstream.Write(data, 0, data.Length);
}
return output.ToArray();
}
}
catch (Exception ERR5)
{
//Logging.LogData($"Failure in Compress: {ERR5.ToString()}");
return null;
}
}
internal static byte[] Decompress(byte[] data)
{
//return data;
try
{
using (MemoryStream output = new MemoryStream())
{
using (MemoryStream input = new MemoryStream(data))
{
using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress))
{
dstream.CopyTo(output);
}
}
return output.ToArray();
}
}
catch (Exception ERR5)
{
//Logging.LogData($"Failure in Decompress: {ERR5.ToString()}");
return null;
}
}
class DataFrame
{
public byte[] bytes { get; set; }
public int bytesRecorded { get; set; }
public string extraData { get; set; } = "";
}
private void btnStop_Click(object sender, EventArgs e)
{
if (Wavein != null)
{
Wavein.StopRecording();
Wavein.Dispose();
Wavein = null;
}
if (Wavein2 != null)
{
Wavein2.StopRecording();
Wavein2.Dispose();
Wavein2 = null;
}
mp3WriterAllSpk = new NAudio.Lame.LameMP3FileWriter(FinalSRFilename, pcm8k16bitMo, 64);
List<DateTime> SpkWrites = AllSpeakerBytes.Select(k => k.Key).OrderBy(c => c).ToList();
foreach (DateTime DT in SpkWrites)
{
byte[] outgoing = Decompress(AllSpeakerBytes[DT].bytes);
mp3WriterAllSpk.Write(outgoing, 0, AllSpeakerBytes[DT].bytesRecorded);
}
mp3WriterAllSpk.Dispose();
SRecFlag = false;
SRecFlag2 = false;
}
This code needs a lot of cleaning up but basically the SyncSpeakers() is being ran every 20 seconds and I am hearing a clipping at 20 second increments of the audio. If I change the timer to run every 10 seconds I will hear a clip sound every 10 seconds of the resulting audio. Any ideas?
I was able to solve this by shaving the first 100 bytes off of the front and replacing them with byte 101. If there is a better way I'd love to hear it.
Thanks!

Optimizing Transposing and Comparison of Concurrent List in Java 8

I have an application in Java 8 Collecting Data of multiple Threads using BlockingQueue.
I need to perform comparison of samples.
But my application is very large, I implemented a mock application (Github) in Java 8.
I'm generating a chunk of bytes (really is random order).
The bytes are stored into ChunkDTO class.
I implemented a capturer the ChunkDTO in a List, code in Capturer class.
Each ChunkDTO of List is translated into a List of Samples (TimePitchValue exactly) returning a nested List (or List of List of TimePitchValue).
Later the nested List is transposed in order to performs comparisons between TimePitchValue with the same time value.
Due to enormous volume of TimePitchValue instances it's consumes huge time in my application.
Here some code (The complete functional Code is in Github) because is still large for this site).
public class Generator {
final static Logger LOGGER = Logger.getLogger("SampleComparator");
public static void main(String[] args) {
long previous = System.nanoTime();
final int minBufferSize = 2048;
int sampleRate = 8192;
int numChannels = 1;
int numBytesPerSample = 1;
int samplesChunkPerSecond = sampleRate / minBufferSize;
int minutes = 0;
int seconds = 10;
int time = 60 * minutes + seconds;
int chunksBySecond = samplesChunkPerSecond * numBytesPerSample * numChannels;
int pitchs = 32;
boolean signed = false;
boolean endianness = false;
AudioFormat audioformat = new AudioFormat(sampleRate, 8 * numBytesPerSample, numChannels, signed, endianness);
ControlDSP controlDSP = new ControlDSP(audioformat);
BlockingQueue<ChunkDTO> generatorBlockingQueue = new LinkedBlockingQueue<>();
Capturer capturer = new Capturer(controlDSP, pitchs, pitchs * time * chunksBySecond, generatorBlockingQueue);
controlDSP.getListFuture().add(controlDSP.getExecutorService().submit(capturer));
for (int i = 0; i < time * chunksBySecond; i++) {
for (int p = 0; p < pitchs; p++) {
ChunkDTO chunkDTO = new ChunkDTO(UtilClass.getArrayByte(minBufferSize), i, p);
LOGGER.info(String.format("chunkDTO: %s", chunkDTO));
try {
generatorBlockingQueue.put(chunkDTO);
} catch (InterruptedException ex) {
LOGGER.info(ex.getMessage());
}
}
try {
Thread.sleep(1000 / chunksBySecond);
} catch (Exception ex) {
}
}
controlDSP.tryFinishThreads(Thread.currentThread());
long current = System.nanoTime();
long interval = TimeUnit.NANOSECONDS.toSeconds(current - previous);
System.out.println("Seconds Interval: " + interval);
}
}
Capturer Class
public class Capturer implements Callable<Void> {
private final ControlDSP controlDSP;
private final int pitchs;
private final int totalChunks;
private final BlockingQueue<ChunkDTO> capturerBlockingQueue;
private final Counter intCounter;
private final Map<Long, List<ChunkDTO>> mapIndexListChunkDTO = Collections.synchronizedMap(new HashMap<>());
private volatile boolean isRunning = false;
private final String threadName;
private static final Logger LOGGER = Logger.getLogger("SampleComparator");
public Capturer(ControlDSP controlDSP, int pitchs, int totalChunks, BlockingQueue<ChunkDTO> capturerBlockingQueue) {
this.controlDSP = controlDSP;
this.pitchs = pitchs;
this.totalChunks = totalChunks;
this.capturerBlockingQueue = capturerBlockingQueue;
this.intCounter = new Counter();
this.controlDSP.getListFuture().add(this.controlDSP.getExecutorService().submit(() -> {
while (intCounter.getValue() < totalChunks) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
capturerBlockingQueue.add(new ChunkDTOStopper());
}));
this.threadName = this.getClass().getSimpleName();
}
#Override
public Void call() throws Exception {
long quantity = 0;
isRunning = true;
while (isRunning) {
try {
ChunkDTO chunkDTO = capturerBlockingQueue.take();
if (chunkDTO instanceof ChunkDTOStopper) {
break;
}
//Find or Create List (according to Index) to add the incoming Chunk
long index = chunkDTO.getIndex();
int sizeChunk = chunkDTO.getChunk().length;
List<ChunkDTO> listChunkDTOWithIndex = getListChunkDTOByIndex(chunkDTO);
//When the List (according to Index) is completed and processed
if (listChunkDTOWithIndex.size() == pitchs) {
mapIndexListChunkDTO.remove(index);
TransposerComparator transposerComparator = new TransposerComparator(controlDSP, controlDSP.getAudioformat(), index, sizeChunk, listChunkDTOWithIndex);
controlDSP.getListFuture().add(controlDSP.getExecutorService().submit(transposerComparator));
}
quantity++;
intCounter.setValue(quantity);
LOGGER.info(String.format("%s\tConsumes:%s\ttotal:%05d", threadName, chunkDTO, quantity));
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
LOGGER.info(String.format("%s\tReceived:%05d\tQty:%s\tPitchs:%s\tEND\n", threadName, quantity, quantity / pitchs, pitchs));
return null;
}
private List<ChunkDTO> getListChunkDTOByIndex(ChunkDTO chunkDTO) {
List<ChunkDTO> listChunkDTOWithIndex = mapIndexListChunkDTO.get(chunkDTO.getIndex());
if (listChunkDTOWithIndex == null) {
listChunkDTOWithIndex = new ArrayList<>();
mapIndexListChunkDTO.put(chunkDTO.getIndex(), listChunkDTOWithIndex);
listChunkDTOWithIndex = mapIndexListChunkDTO.get(chunkDTO.getIndex());
}
listChunkDTOWithIndex.add(chunkDTO);
return listChunkDTOWithIndex;
}
}
TransposerComparator class.
The optimization required is in this code, specifically on transposedNestedList method.
public class TransposerComparator implements Callable<Void> {
private final ControlDSP controlDSP;
private final AudioFormat audioformat;
private final long index;
private final int sizeChunk;
private final List<ChunkDTO> listChunkDTOWithIndex;
private final String threadName;
private static final Logger LOGGER = Logger.getLogger("SampleComparator");
public TransposerComparator(ControlDSP controlDSP, AudioFormat audioformat, long index, int sizeChunk, List<ChunkDTO> listChunkDTOWithIndex) {
this.controlDSP = controlDSP;
this.audioformat = audioformat;
this.index = index;
this.sizeChunk = sizeChunk;
this.listChunkDTOWithIndex = listChunkDTOWithIndex;
this.threadName = this.getClass().getSimpleName() + "_" + String.format("%05d", index);
}
#Override
public Void call() throws Exception {
Thread.currentThread().setName(threadName);
LOGGER.info(String.format("%s\tINI", threadName));
try {
int numBytesPerSample = audioformat.getSampleSizeInBits() / 8;
int quantitySamples = sizeChunk / numBytesPerSample;
long baseTime = quantitySamples * index;
// Convert the List of Chunk Bytes to Nested List of TimePitchValue
List<List<TimePitchValue>> nestedListTimePitchValue = listChunkDTOWithIndex
.stream()
.map(chunkDTO -> {
return IntStream
.range(0, quantitySamples)
.mapToObj(time -> {
int value = extractValue(chunkDTO.getChunk(), numBytesPerSample, time);
return new TimePitchValue(chunkDTO.getPitch(), baseTime + time, value);
}).collect(Collectors.toList());
}).collect(Collectors.toList());
List<List<TimePitchValue>> timeNestedListTimePitchValue = transposedNestedList(nestedListTimePitchValue);
} catch (Exception ex) {
ex.printStackTrace();
LOGGER.log(Level.SEVERE, null, ex);
throw ex;
}
return null;
}
private static int extractValue(byte[] bytesSamples, int numBytesPerSample, int time) {
byte[] bytesSingleNumber = Arrays.copyOfRange(bytesSamples, time * numBytesPerSample, (time + 1) * numBytesPerSample);
int value = numBytesPerSample == 2
? (UtilClass.Byte2IntLit(bytesSingleNumber[0], bytesSingleNumber[1]))
: (UtilClass.byte2intSmpl(bytesSingleNumber[0]));
return value;
}
private static List<List<TimePitchValue>> transposedNestedList(List<List<TimePitchValue>> nestedList) {
List<List<TimePitchValue>> outNestedList = new ArrayList<>();
nestedList.forEach(pitchList -> {
pitchList.forEach(pitchValue -> {
List<TimePitchValue> listTimePitchValueWithTime = listTimePitchValueWithTime(outNestedList, pitchValue.getTime());
if (!outNestedList.contains(listTimePitchValueWithTime)) {
outNestedList.add(listTimePitchValueWithTime);
}
listTimePitchValueWithTime.add(pitchValue);
});
});
outNestedList.forEach(pitchList -> {
pitchList.sort(Comparator.comparingInt(TimePitchValue::getValue).reversed());
});
return outNestedList;
}
private static List<TimePitchValue> listTimePitchValueWithTime(List<List<TimePitchValue>> nestedList, long time) {
List<TimePitchValue> listTimePitchValueWithTime = nestedList
.stream()
.filter(innerList -> innerList.stream()
.anyMatch(timePitchValue -> timePitchValue.getTime() == time))
.findAny()
.orElseGet(ArrayList::new);
return listTimePitchValueWithTime;
}
}
I was testing:
With 5 Seconds in Generator class and the List<List<TimePitchValue>> timeNestedListTimePitchValue = transposedNestedList(nestedListTimePitchValue); line in TransposerComparator class, Commented 7 Seconds needed, Uncommented 211 Seconds needed.
With 10 Seconds in Generator class and the List<List<TimePitchValue>> timeNestedListTimePitchValue = transposedNestedList(nestedListTimePitchValue); line in TransposerComparator class, Commented 12 Seconds needed, Uncommented 574 Seconds needed.
I need to use the application at least 60 minutes.
With the purpose of reduce the needed (consumed) time, I have two ways:
I choose for short is to optimize the methods that I am currently using.
That should be successful but longer and is to use GPGPU, but I don't know where to start implementing it yet.
QUESTIONS
This Question is for the first way: What changes do you recommend in the code of the transposedNestedList method in order to improve speed?
Is there better alternative to use this Comparison?
outNestedList.forEach(pitchList -> {
pitchList.sort(Comparator.comparingInt(TimePitchValue::getValue).reversed());
});

Rate limiting with redis

I'm using elasticache redis for rate limit and use Redisson as the client, the related code is:
public CompletableFuture<List<Long>> incrementSingleKeys(
List<String> keys, List<Long> increments, List<Long> ttls) {
RBatch batch = redissonClient.createBatch(BatchOptions.defaults());
for (int i = 0; i < keys.size(); i++) {
batch.getAtomicLong(keys.get(i)).addAndGetAsync(increments.get(i));
}
return batch
.executeAsync()
.thenCompose(
(counters) -> {
List<String> keysToSet = Lists.newArrayList();
List<Long> TTLsToSet = Lists.newArrayList();
for (int i = 0; i < counters.size(); i++) {
if (counters.get(i) == increments.get(i)) { // only set ttl for new keys
keysToSet.add(keys.get(i));
TTLsToSet.add(ttls.get(i));
}
}
if (!keysToSet.isEmpty()) { // Call setTTLS
return setTTLs(keysToSet, TTLsToSet)
.thenApply(
(r) -> counters
);
} else {
return CompletableFuture.completedFuture(counters);
}
});
}
public CompletableFuture<List<Boolean>> setTTLs(List<String> keys, List<Long> TTLs) {
CompletableFuture<List<Boolean>> future = new CompletableFuture<>();
Stopwatch timer = Stopwatch.createStarted();
RBatch batch = redissonClient.createBatch(BatchOptions.defaults());
for (int i = 0; i < keys.size(); i++) {
batch.getBucket(keys.get(i)).expireAsync(TTLs.get(i), TimeUnit.MILLISECONDS);
}
batch
.executeAsync()
.whenComplete(
(list, ex) -> {
if (ex != null) {
future.complete(
Collections.nCopies(size, false); // fail open
)
} else {
future.complete(
list.stream()
.map(entry -> (entry instanceof Boolean ? (Boolean) entry : false))
.collect(Collectors.toList()),
);
}
});
return future;
}
Basically, I'll set ttl only for new keys. The issue is that sometimes the increment batch call is successful but setTTL call timeout, which results in a permanent key and could lead to incorrect rate limiting. One workaround is to always get and set ttl whenever increment happens, but this would affect the performance. Is there any other solution?