I want to create a julia "server" that contains a cached value and serves that when requested and updates the value when an update arrives from other channels
My plan to do this is:
Use julia ZMQ (zeromq) which listens on a REP (reply) socket and delivers the value to any request coming to that REP socket.
Also, the program has a SUB (subscribe) socket that updates the value whenever the socket receives anything
The REP socket blocks using ZMQ.recv.
Maybe the SUB socket does as well, not sure
But basically, both parts need to run independently in a while loop, sharing some memory (variable)
So perhaps this needs to be done using SharedArrays, spawning processes
But I just can't figure out how to do this in code.
E.g. I can #spawn each such process, one has a REP, one has a SUB socket, but I don't know how to get their pid's to create a SharedArray
Can someone help please?
I am also open to different design solutions to solve the problem (basically data is being constantly updated from some source and other programs need to be able to get the most current copy of this data)
Thanks
Imran
EDIT:
I have kind of gotten a simple version to work as follows:
It has 2 independent REP/REQ sockets
The strange thing is that this sometimes works and sometimes after a few calls to readcache() and writecache(41) blocks in either the readcache or the writecache ... but I cannot reproduce, as it sometimes just works smoothly
Is this the correct way of solving this in julia?
using ZMQ
type CT
a::Int
b::String
end
ct = CT(1,"a")
readport = 5551
readproc = #spawn readcacheproc(ct,readport)
writeport = 5552
writeproc = #spawn writecacheproc(ct,writeport)
# test as follows
# readcache() # expect [1 a]
# writecache("test") # expect [4 test]
# readcache() # expect [4 test]
function readcache(port=readport)
ctx=Context()
s=Socket(ctx,REQ)
ZMQ.connect(s,"tcp://localhost:$port")
ZMQ.send(s,"")
println(bytestring(ZMQ.recv(s)))
ZMQ.close(s)
ZMQ.close(ctx)
end
function writecache(value,port=writeport)
ctx=Context()
s=Socket(ctx,REQ)
ZMQ.connect(s,"tcp://localhost:$port")
ZMQ.send(s,"$value")
println(bytestring(ZMQ.recv(s)))
ZMQ.close(s)
ZMQ.close(ctx)
end
function readcacheproc(cache,port=readport)
ctx=Context()
s=Socket(ctx,REP)
ZMQ.bind(s,"tcp://*:$port")
done = false
while !done
msg = bytestring(ZMQ.recv(s)) # actual msg is ignored
ZMQ.send(s,"$(cache.a) $(cache.b)")
end
ZMQ.close(s)
ZMQ.close(ctx)
end
function writecacheproc(cache,port=writeport)
ctx=Context()
s=Socket(ctx,REP)
ZMQ.bind(s,"tcp://*:$port")
done = false
while !done
msg = bytestring(ZMQ.recv(s))
cache.a = length(msg)
cache.b = msg
ZMQ.send(s,"new cache: $(cache.a) $(cache.b)")
end
ZMQ.close(s)
ZMQ.close(ctx)
end
The following seems to work, although I dont know whether this is the best way of solving it
using ZMQ
type CT
a::Int
b::String
end
ct = CT(1,"a")
readport = 5551
readproc = #spawn readcacheproc(ct,readport)
writeport = 5552
writeproc = #spawn writecacheproc(ct,writeport)
# test as follows
# readcache() # expect [1 a]
# writecache("test") # expect [4 test]
# readcache() # expect [4 test]
function readcache(port=readport)
ctx=Context()
s=Socket(ctx,REQ)
ZMQ.connect(s,"tcp://localhost:$port")
ZMQ.send(s,"")
println(bytestring(ZMQ.recv(s)))
ZMQ.close(s)
ZMQ.close(ctx)
end
function writecache(value,port=writeport)
ctx=Context()
s=Socket(ctx,REQ)
ZMQ.connect(s,"tcp://localhost:$port")
ZMQ.send(s,"$value")
println(bytestring(ZMQ.recv(s)))
ZMQ.close(s)
ZMQ.close(ctx)
end
function readcacheproc(cache,port=readport)
ctx=Context()
s=Socket(ctx,REP)
ZMQ.bind(s,"tcp://*:$port")
done = false
while !done
msg = bytestring(ZMQ.recv(s)) # actual msg is ignored
ZMQ.send(s,"$(cache.a) $(cache.b)")
end
ZMQ.close(s)
ZMQ.close(ctx)
end
function writecacheproc(cache,port=writeport)
ctx=Context()
s=Socket(ctx,REP)
ZMQ.bind(s,"tcp://*:$port")
done = false
while !done
msg = bytestring(ZMQ.recv(s))
cache.a = length(msg)
cache.b = msg
ZMQ.send(s,"new cache: $(cache.a) $(cache.b)")
end
ZMQ.close(s)
ZMQ.close(ctx)
end
Related
I am in the process of learning Julia and I'd like to do some buffer manipulation.
What I want to achieve is the following:
I've got a buffer that I can write to and read from at the same time, meaning that the speed with which I add a value to the Fifo buffer approximately equals the speed with which I read from the buffer. Reading and writing will happen in separate threads so it can occur simultaneously.
Additionally, I want to be able to control the values that I write into the buffer based on user input. For now, this is just a simple console prompt asking for a number, which I then want to write into the stream continously. The prompt refreshes and asks for a new number to write into the stream, but the prompt is non-blocking, meaning that in the background, the old number is written to the buffer until I enter a new number, which is then written to the buffer continuously.
This is my preliminary code for simulatenous reading and writing of the stream:
using Causal
CreateBuffer(size...) = Buffer{Fifo}(Float32, size...)
function writetobuffer(buf::Buffer, n::Float32)
while !isfull(buf)
write!(buf, fill(n, 2, 1))
end
end
function readfrombuffer(buf::Buffer)
while true
while !isempty(buf)
#show read(buf)
end
end
end
n_channels = 2
sampling_rate = 8192
duration = 2
n_frames = sampling_rate * duration
sbuffer = CreateBuffer(n_channels, n_frames)
print("Please enter a number: ")
n = parse(Float32, readline())
s1 = Threads.#spawn writetobuffer(sbuffer, n)
s2 = Threads.#spawn readfrombuffer(sbuffer)
s1 = fetch(s1)
s2 = fetch(s2)
I am not sure how to integrate the user input in a way that it keeps writing and reading the latest number the user put in. I looked at the documentation for channels, but didn't manage to get it working in a way that was non-blocking for the stream writing. I don't know that the correct approach is (channels, events, julia's multithreading) to enable this functionality.
How would I go on about to include this?
I managed to get it working, but I think it could be improved:
using Causal
CreateBuffer(size...) = Buffer{Fifo}(Float32, size...)
function writeToBuffer(buf::Buffer, n::Float32)
write!(buf, fill(n, 2, 1))
end
function readFromBuffer()
global soundbuffer
println("Starting")
sleep(0.5)
while true
while !isempty(soundbuffer)
read(soundbuffer)
end
end
println("Exiting...")
end
function askForInput()::Float32
print("Please enter a number: ")
a = parse(Float32, readline())
return(a)
end
function inputAndWrite()
global soundbuffer
old_num::Float32 = 440
new_num::Float32 = 440
while true
#async new_num = askForInput()
while (new_num == old_num)
writeToBuffer(soundbuffer, new_num)
end
old_num = new_num
println("Next iteration with number " * string(new_num))
end
end
n_channels = 2
sampling_rate = 8192
duration = 2
n_frames = sampling_rate * duration
soundbuffer = CreateBuffer(n_channels, n_frames)
s1 = Threads.#spawn inputAndWrite()
s2 = Threads.#spawn readFromBuffer()
s1 = fetch(s1)
s2 = fetch(s2)
When I call consume(generator) I get this error. Is this a version problem?
function fib()
a = 0
produce(a)
b = 1
produce(b)
while true
a , b = b , a+b
produce(b)
end
end
generator = Task(fib)
consume(generator)
Here a way to do something similar using a Channel
global channel = Channel{Int}(1)
function fib()
# global here is not needed because we aren't modifying the channel handle, just the contents
# global channel
a = 0
put!(channel, a)
b = 1
put!(channel, b)
while true
a , b = b , a+b
put!(channel,b)
sleep(1)
end
end
#async while true; item = take!(channel); println("item: $item"); end
#async fib()
Note that #async will hide errors, so you may want to do a try catch with showerror(stderr, e, catch_backtrace()) if things are not running.
Each #async produces a Task handle.
Also the put! and take! will block when the channel if filled up. You may want to expand the channel size to handle a larger buffer.
I'd like to run heavy computations in Julia for a fixed duration, for example 10 seconds. I tried this:
timer = Timer(10.0)
while isopen(timer)
computation()
end
But this does not work, since the computations never let Julia's task scheduler take control. So I added yield() in the loop:
timer = Timer(10.0)
while isopen(timer)
yield()
computation()
end
But now there is significant overhead from calling yield(), especially when one call to computation() is short. I guess I could call yield() and isopen() only every 1000 iterations or so, but I would prefer a solution where I would not have to tweak the number of iterations every time I change the computations. Any ideas?
This pattern below uses threads and on my laptop has a latency of around 35ms for each 1,000,000 calls which is more than acceptable for any job.
Tested on Julia 1.5 release candidate:
function should_stop(timeout=10)
handle = Threads.Atomic{Bool}(false)
mytask = Threads.#spawn begin
sleep(timeout)
Threads.atomic_or!(handle, true)
end
handle
end
function do_some_job_with_timeout()
handle = should_stop(5)
res = BigInt() # save results to some object
mytask = Threads.#spawn begin
for i in 1:10_000_000
#TODO some complex computations here
res += 1 # mutate the result object
handle.value && break
end
end
wait(mytask) # wait for the job to complete
res
end
You can also used Distributed instead. The code below seems to have a much better latency - only about 1ms for each 1,000,000 timeout checks.
using Distributed
using SharedArrays
addprocs(1)
function get_termination_handle(timeout=5,workerid::Int=workers()[end])::SharedArray{Bool}
handle = SharedArray{Bool}([false])
proc = #spawnat workerid begin
sleep(timeout)
handle[1]=true
end
handle
end
function fun_within_timeout()
res = 0
h = get_termination_handle(0.1)
for i = 1:100_000_000
res += i % 2 == 0 ? 1 : 0
h[1] && break
end
res
end
I am a Julia (and stackoverflow) newbie but am trying unsuccessfully to simplify a function call.
I need to define calls to create many instances of 30 different structs, each having a different set of properties.
The code below works but will force the user to use exactly the same string twice, as in:
EV_668876 = newEV("EV_668876", "test EV")
This is a pain and likely to cause errors.
I have written a macro to generate the command but can't get REPL to execute the command.
Here is the code (sorry for its length).
mutable struct EV
label::FixedLabel
id::FixedId
name::String
designAuth::Ident
descripn::String
timestamp::String
priority::Int16
assoc_EO::Ident # this needs a new Set of EOstructs, to be defined
origin_OV::Ident # similar Set of OVstructs
OV_destination::Ident # this needs a new OVstruct, to be defined
underRespOf::Ident
underAuthOf::Ident
end
function newEV(id::String, name::String)
trylabel = String(split(id,['-',':','_'])[1]) # Note that split() yields a SubString(String)
if trylabel !== "EV" # ! => not
throw(DomainError("This id $id is not an EV, try again"))
end
labelFixed = FixedLabel(trylabel)
registerId(id) # registers id if OK
idFixed = FixedId(id)
# ident = newId(id,name)
new = EV(labelFixed,idFixed,name,anon_anon,"","",0,anon_anon,anon_anon,anon_anon,anon_anon,anon_anon)
end
EV_668876 = newEV("EV_668876", "test EV") # runs OK and produces
#=
This runs OK and produces
EV_668876 registered OK
EV(FixedLabel("EV"), FixedId("EV_668876"), "test EV", Ident(FixedLabel("PPR"), FixedId("PPR-2"), "Anon_Anon"), "", "", 0, Ident(FixedLabel("PPR"), FixedId("PPR-2"), "Anon_Anon"), Ident(FixedLabel("PPR"), FixedId("PPR-2"), "Anon_Anon"), Ident(FixedLabel("PPR"), FixedId("PPR-2"), "Anon_Anon"), Ident(FixedLabel("PPR"), FixedId("PPR-2"), "Anon_Anon"), Ident(FixedLabel("PPR"), FixedId("PPR-2"), "Anon_Anon"))
#=
# === Attempting to use a macro to simplify the newEV() function ===
macro create(id,name)
label = "EV"
return :(println($id," = new",$label,"(\"",$id,"\", \"",$name,"\")"))
end
#create("EV_97234894","new test")
#=
This generates
EV_97234894 = newEV("EV_97234894", "new test") which is what I want
but returns a type nothing – is that why REPL doesn't execute the result?
#=
# ==============================================
As far as I understand (and I'm not sure what the printing does in your example), you want a macro that expands
#create <id> <name>
to
<id> = newEV("<id>", name)
The following will achieve that:
julia> macro create(id::Symbol, name)
:($(esc(id)) = newEV($(String(id)), $name))
end
#create (macro with 1 method)
julia> #macroexpand #create EV_234324 "new test"
:(EV_234324 = (Main.newEV)("EV_234324", "new test"))
#macroexpand is for debugging, since I didn't copy your code. It simply gets the expression that results from a macro call.
escaping is necessary here, since you want the identifier given by the symbol id to end up being defined in the calling scope.
what is the prefered way to investigate or print out further detail (print input variable of a function, iteration number, etc.) of a failed #test inside a #testset?
I tried to wrap a try-catch-block around it. However, it doesn't seem to fire.
Here is a made-up example:
using Base.Test
rng = MersenneTwister(3231);
# define function that works different than expected
function compare(a,b)
if a == 3 && b == 3
return false
else
return a == b
end
end
# test function in a test set
#testset "Test Compare Function" begin
for iii = 1:10
number = rand(rng,1:10)
try
#test compare(number,number) == true
catch
#show(number)
end
end
end
Thank you very much!
You need to make sure it tests after the printing.
#testset "Test Compare Function" begin
for iii = 1:10
number = rand(rng,1:10)
#test begin
res = compare(number,number) == true
if !res
#show number
flush(STDOUT)
end
res
end
end
end