If there is a choice in programming language, one option can be to use Julia, which has built in support for sparse matrices (via Suitsparse). The timing come out to about one and a half milliseconds on my laptop, and you get to use an interactive, dynamic language, which may be useful in certain situations.
julia> a=sprand(1000, 1000, 0.1)
1000x1000 sparse matrix with 99749 Float64 entries:
[5 , 1] = 0.725824
[17 , 1] = 0.420022
[19 , 1] = 0.404282
[21 , 1] = 0.0307138
[52 , 1] = 0.453376
[55 , 1] = 0.30054
[69 , 1] = 0.360203
[74 , 1] = 0.346881
[94 , 1] = 0.312849
⋮
[932 , 1000] = 0.978966
[933 , 1000] = 0.149551
[954 , 1000] = 0.417852
[959 , 1000] = 0.722707
[964 , 1000] = 0.519931
[967 , 1000] = 0.567152
[971 , 1000] = 0.964192
[979 , 1000] = 0.88494
[987 , 1000] = 0.286723
[988 , 1000] = 0.24282
julia> b=sprand(1000, 1000, 0.1)
1000x1000 sparse matrix with 99998 Float64 entries:
[1 , 1] = 0.920533
[3 , 1] = 0.879179
[7 , 1] = 0.267203
[25 , 1] = 0.522407
[34 , 1] = 0.656031
[41 , 1] = 0.280885
[44 , 1] = 0.735824
[68 , 1] = 0.433098
[69 , 1] = 0.124862
⋮
[932 , 1000] = 0.505959
[939 , 1000] = 0.983413
[947 , 1000] = 0.418157
[949 , 1000] = 0.884657
[963 , 1000] = 0.412645
[964 , 1000] = 0.544348
[966 , 1000] = 0.709398
[983 , 1000] = 0.260483
[989 , 1000] = 0.1218
[1000, 1000] = 0.468975
julia> a.*b
1000x1000 sparse matrix with 9876 Float64 entries:
[69 , 1] = 0.0449757
[102 , 1] = 0.0340867
[137 , 1] = 0.0794594
[247 , 1] = 0.108002
[376 , 1] = 0.248346
[609 , 1] = 0.241789
[633 , 1] = 0.224115
[658 , 1] = 0.379804
[754 , 1] = 0.272618
⋮
[224 , 1000] = 0.0122434
[301 , 1000] = 0.163899
[309 , 1000] = 0.0972784
[403 , 1000] = 0.0245688
[659 , 1000] = 0.0801249
[700 , 1000] = 0.158823
[760 , 1000] = 0.388442
[926 , 1000] = 0.193808
[932 , 1000] = 0.495317
[964 , 1000] = 0.283024
julia> @time a.*b
0.001649 seconds (25 allocations: 3.056 MB)